Skip to main content
Skip to main content
Edit this page

Helm additional manifests

Chart version 2.x only

The additionalManifests feature is available in the v2.x subchart-based Helm chart only.

The additionalManifests value lets you deploy arbitrary Kubernetes objects alongside the ClickStack chart. Use it for resources that the chart does not template natively, such as NetworkPolicy, HorizontalPodAutoscaler, ServiceAccount, PodMonitor, custom Ingress objects, or any other Kubernetes API object.

How it works

Each entry in additionalManifests is a complete Kubernetes resource definition. The chart:

  1. Iterates over each entry in the list
  2. Converts the entry to YAML (toYaml)
  3. Evaluates template expressions in that YAML using Helm tpl

Template expressions can reference:

  • .Release.Name, .Release.Namespace
  • include "clickstack.fullname" . and other chart helpers
  • .Values.*
additionalManifests:
  - apiVersion: v1
    kind: ConfigMap
    metadata:
      name: '{{ include "clickstack.fullname" . }}-custom'
    data:
      release: '{{ .Release.Name }}'

Values file constraints

additionalManifests is configured in a values file, and values files are parsed as YAML before tpl runs.

  • Any {{ ... }} in a values file must be inside a quoted string
  • Structural template blocks are not valid values YAML (for example, {{- include ... | nindent ... }} by itself)
  • For non-string fields (for example, numeric ports), use literal values or named ports
  • If you need structural templating, use a wrapper chart template instead of a raw values file
# Valid in values.yaml
name: '{{ include "clickstack.fullname" . }}-app'

# Invalid in values.yaml (unquoted template expression)
name: {{ include "clickstack.fullname" . }}-app

# Invalid in values.yaml (structural template block)
labels:
  {{- include "clickstack.labels" . | nindent 2 }}

Available chart helpers

These helpers are defined in templates/_helpers.tpl:

HelperDescriptionValues-file usage
clickstack.nameChart name (truncated to 63 chars)Safe in quoted scalars
clickstack.fullnameRelease-qualified nameSafe in quoted scalars
clickstack.chartChart name + versionSafe in quoted scalars
clickstack.selectorLabelsSelector labels blockWrapper chart templates only
clickstack.labelsStandard labels blockWrapper chart templates only
clickstack.mongodb.fullnameMongoDB CR nameSafe in quoted scalars
clickstack.clickhouse.fullnameClickHouse CR nameSafe in quoted scalars
clickstack.otel.fullnameOTEL Collector nameSafe in quoted scalars

Examples

ServiceAccount

additionalManifests:
  - apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: '{{ include "clickstack.fullname" . }}'
      namespace: '{{ .Release.Namespace }}'
      labels:
        app.kubernetes.io/name: '{{ include "clickstack.name" . }}'
        app.kubernetes.io/instance: '{{ .Release.Name }}'
      annotations:
        eks.amazonaws.com/role-arn: "arn:aws:iam::123456789:role/my-role"

NetworkPolicy

Restrict ingress traffic to HyperDX pods:

additionalManifests:
  - apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: '{{ include "clickstack.fullname" . }}-allow-ingress'
    spec:
      podSelector:
        matchLabels:
          app.kubernetes.io/name: '{{ include "clickstack.name" . }}'
          app.kubernetes.io/instance: '{{ .Release.Name }}'
      policyTypes:
        - Ingress
      ingress:
        - from:
            - namespaceSelector:
                matchLabels:
                  kubernetes.io/metadata.name: ingress-nginx
          ports:
            - protocol: TCP
              port: 3000
            - protocol: TCP
              port: 8000

HorizontalPodAutoscaler

additionalManifests:
  - apiVersion: autoscaling/v2
    kind: HorizontalPodAutoscaler
    metadata:
      name: '{{ include "clickstack.fullname" . }}-hpa'
    spec:
      scaleTargetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: '{{ include "clickstack.fullname" . }}-app'
      minReplicas: 2
      maxReplicas: 10
      metrics:
        - type: Resource
          resource:
            name: cpu
            target:
              type: Utilization
              averageUtilization: 75

PodMonitor (Prometheus Operator)

additionalManifests:
  - apiVersion: monitoring.coreos.com/v1
    kind: PodMonitor
    metadata:
      name: '{{ include "clickstack.fullname" . }}'
      labels:
        release: prometheus
    spec:
      selector:
        matchLabels:
          app.kubernetes.io/name: '{{ include "clickstack.name" . }}'
          app.kubernetes.io/instance: '{{ .Release.Name }}'
      podMetricsEndpoints:
        - port: app
          interval: 30s

AWS ALB Ingress

When using the AWS Load Balancer Controller, disable the chart's built-in nginx Ingress and define a custom ALB Ingress:

hyperdx:
  ingress:
    enabled: false

additionalManifests:
  - apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: '{{ include "clickstack.fullname" . }}-alb'
      annotations:
        alb.ingress.kubernetes.io/scheme: internet-facing
        alb.ingress.kubernetes.io/target-type: ip
        alb.ingress.kubernetes.io/certificate-arn: "arn:aws:acm:us-east-1:123456789:certificate/abc-123"
        alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
        alb.ingress.kubernetes.io/ssl-redirect: "443"
        alb.ingress.kubernetes.io/group.name: clickstack
        alb.ingress.kubernetes.io/healthcheck-path: /api/health
    spec:
      ingressClassName: alb
      rules:
        - host: clickstack.example.com
          http:
            paths:
              - path: /
                pathType: Prefix
                backend:
                  service:
                    name: '{{ include "clickstack.fullname" . }}-app'
                    port:
                      name: app

For a complete ALB setup example including internal OTEL collector ingress and HPA, see the ALB example values.

TargetGroupBinding

For ALB scenarios that require explicit TargetGroupBinding resources:

additionalManifests:
  - apiVersion: elbv2.k8s.aws/v1beta1
    kind: TargetGroupBinding
    metadata:
      name: '{{ include "clickstack.fullname" . }}-tgb'
    spec:
      serviceRef:
        name: '{{ include "clickstack.fullname" . }}-app'
        port: app
      targetGroupARN: "arn:aws:elasticloadbalancing:us-east-1:123456789:targetgroup/my-tg/abc123"
      targetType: ip

Advanced: wrapper chart templates

If you need structural helpers like include "clickstack.labels" . | nindent 4, render them from a wrapper chart template (templates/*.yaml) instead of putting those blocks directly in values files.

Example wrapper chart template snippet:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ include "clickstack.fullname" . }}-extra
  labels:
    {{- include "clickstack.labels" . | nindent 4 }}
data:
  appPort: "{{ .Values.hyperdx.ports.app }}"

Tips

Helm hooks

Each additionalManifests entry is rendered as a separate YAML document. You can add Helm hook annotations to control install/upgrade ordering:

additionalManifests:
  - apiVersion: batch/v1
    kind: Job
    metadata:
      name: post-install-job
      annotations:
        helm.sh/hook: post-install
        helm.sh/hook-delete-policy: hook-succeeded
    spec:
      template:
        spec:
          restartPolicy: Never
          containers:
            - name: migrate
              image: my-migration-image:latest
              command: ["./migrate.sh"]

CRD ordering

If your additional manifests include custom resources (for example, PodMonitor), the CRDs must already exist in the cluster before install/upgrade.

Combining multiple resources

additionalManifests is a list. Entries are rendered in list order, and each entry becomes its own YAML document.

Next steps