# Helmfile Components

Helmfile components manage Kubernetes deployments using Helmfile, a
declarative spec for deploying Helm charts. They allow you to manage Helm
releases with the same stack-based configuration approach used for Terraform.

:::tip Prefer native Helm for most new chart deployments
Helmfile remains a great choice when you need its advanced orchestration
features. If you are not relying on Helmfile-specific features, use native
[`atmos helm`](/cli/commands/helm/usage) components instead. Atmos now handles
stack-based values, inheritance, secrets, dependencies, hooks, provisioning
targets, and CI summaries directly for Helm charts.
:::

## Available Configuration Sections

Helmfile components support the common configuration sections:

- **[`vars`](/stacks/vars)**
  Variables passed to helmfile.
- **[`env`](/stacks/env)**
  Environment variables during execution.
- **[`settings`](/stacks/settings)**
  Integrations and metadata.
- **[`metadata`](/stacks/components/component-metadata)**
  Component behavior and inheritance.
- **[`command`](/stacks/command)**
  Override helmfile binary.
- **[`hooks`](/stacks/hooks)**
  Lifecycle event handlers.

## Component Structure

A typical Helmfile component configuration:

```yaml
components:
  helmfile:
    nginx-ingress:
      metadata:
        component: nginx-ingress
      vars:
        chart_version: "4.7.1"
        replica_count: 3
        service_type: LoadBalancer
      env:
        KUBECONFIG: '{{ env "HOME" }}/.kube/config'
      dependencies:
        components:
          - name: eks
            kind: terraform
```

## Helmfile Directory Structure

Helmfile components are located in the path configured in `atmos.yaml`:

```yaml
# atmos.yaml
components:
  helmfile:
    base_path: components/helmfile
    kubeconfig_path: /dev/shm
    helm_aws_profile_pattern: "{namespace}-{tenant}-gbl-{stage}-helm"
    cluster_name_pattern: "{namespace}-{tenant}-{environment}-{stage}-eks-cluster"
```

Example structure:

```
components/helmfile/
├── nginx-ingress/
│   └── helmfile.yaml
├── cert-manager/
│   └── helmfile.yaml
└── prometheus/
    └── helmfile.yaml
```

## Helmfile Template

Each helmfile component contains a `helmfile.yaml` that uses variables passed from Atmos:

**File:** `components/helmfile/nginx-ingress/helmfile.yaml`

```yaml
repositories:
  - name: ingress-nginx
    url: https://kubernetes.github.io/ingress-nginx

releases:
  - name: nginx-ingress
    namespace: ingress-nginx
    createNamespace: true
    chart: ingress-nginx/ingress-nginx
    version: {{ .Values.chart_version | quote }}
    wait: true
    values:
      - controller:
          replicaCount: {{ .Values.replica_count }}
          service:
            type: {{ .Values.service_type }}
          metrics:
            enabled: {{ .Values.metrics_enabled | default false }}
```

## Component-Type Defaults

Define defaults for all Helmfile components:

```yaml
# Apply to all Helmfile components
helmfile:
  vars:
    timeout: 600
    atomic: true
    wait: true
  env:
    HELM_CACHE_HOME: /tmp/helm-cache
    HELM_CONFIG_HOME: /tmp/helm-config

# Individual components
components:
  helmfile:
    nginx-ingress:
      vars:
        chart_version: "4.7.1"
```

## Complete Example

**File:** `stacks/orgs/acme/plat/prod/us-east-1.yaml`

```yaml
import:
  - catalog/helmfile/_defaults
  - orgs/acme/plat/prod/_defaults

vars:
  region: us-east-1
  stage: prod
  eks_cluster_name: acme-prod-eks

helmfile:
  vars:
    timeout: 900
    atomic: true
  env:
    AWS_PROFILE: acme-prod
    KUBECONFIG: "/tmp/kubeconfig-acme-prod"

components:
  helmfile:
    cert-manager:
      metadata:
        component: cert-manager
      vars:
        chart_version: "v1.13.0"
        install_crds: true

    nginx-ingress:
      metadata:
        component: nginx-ingress
      vars:
        chart_version: "4.7.1"
        replica_count: 3
        service_type: LoadBalancer
        metrics_enabled: true
      dependencies:
        components:
          - name: cert-manager

    prometheus:
      metadata:
        component: prometheus
        inherits:
          - prometheus/defaults
      vars:
        chart_version: "45.0.0"
        retention: "15d"
        storage_size: "100Gi"
      dependencies:
        components:
          - name: nginx-ingress
```

## Running Helmfile Commands

Atmos provides commands that wrap Helmfile operations:

```bash
# Diff changes
atmos helmfile diff nginx-ingress -s plat-ue1-prod

# Apply changes
atmos helmfile apply nginx-ingress -s plat-ue1-prod

# Sync releases
atmos helmfile sync nginx-ingress -s plat-ue1-prod

# Destroy releases
atmos helmfile destroy nginx-ingress -s plat-ue1-prod

# Run any helmfile subcommand
atmos helmfile <subcommand> <component> -s <stack>
```

## Environment Variables

Common environment variables for Helmfile components:

- **`KUBECONFIG`**
  Path to kubeconfig file.
- **`HELM_CACHE_HOME`**
  Helm cache directory.
- **`HELM_CONFIG_HOME`**
  Helm configuration directory.
- **`HELM_DATA_HOME`**
  Helm data directory.
- **`AWS_PROFILE`**
  AWS profile for EKS authentication.
- **`HELM_EXPERIMENTAL_OCI`**
  Enable OCI registry support.

## Passing Secrets Securely

Helmfile writes the `vars` section to a [varfile **on disk** in plaintext](/cli/configuration/secrets)
(world‑readable, `0644`) so Helmfile can read it. Unlike Terraform — which keeps secret‑bearing
`vars` off disk — Helmfile performs **no** off‑disk partitioning. **Never put secrets in a Helmfile
component's `vars` section.**

:::warning Secrets belong in `env`, not `vars`
Values in the `env` section are injected only into the Helmfile subprocess environment and are
**never written to disk**. Declare secrets under [`secrets.vars`](/cli/configuration/secrets#declaring-secrets),
reference them with [`!secret`](/functions/yaml/secret) in `env`, and read them inside `helmfile.yaml`
with Helmfile's `requiredEnv` function.
:::

A simple, self‑contained example:

**File:** `stacks/deploy/prod.yaml`

```yaml
components:
  helmfile:
    my-app:
      secrets:
        vars:
          DB_PASSWORD:
            description: "Application database password"
            store: ssm-secrets        # a `secret: true` store; or use `sops: <provider>`
            required: true
      env:
        # Resolved at runtime and injected ONLY into the Helmfile subprocess
        # environment — never written to the varfile on disk.
        DB_PASSWORD: !secret DB_PASSWORD
      vars:
        chart_version: "1.2.3"         # non-secret values only
```

Read the secret from the environment in `helmfile.yaml` — not from `.Values` (which comes from the
on‑disk varfile):

**File:** `components/helmfile/my-app/helmfile.yaml`

```yaml
releases:
  - name: my-app
    chart: my-repo/my-app
    version: {{ .Values.chart_version | quote }}
    values:
      - database:
          # `requiredEnv` reads the secret from the environment and fails fast if it is unset.
          # Use `env "DB_PASSWORD"` instead if the variable is optional.
          password: {{ requiredEnv "DB_PASSWORD" | quote }}
```

## Integration with Terraform

Helmfile components often depend on infrastructure created by Terraform:

```yaml
components:
  terraform:
    eks:
      vars:
        cluster_name: acme-prod-eks

  helmfile:
    nginx-ingress:
      vars:
        # Reference Terraform outputs
        cluster_name: "{{ .terraform_outputs.eks.cluster_name }}"
      env:
        KUBECONFIG: "{{ .terraform_outputs.eks.kubeconfig_path }}"
      dependencies:
        components:
          - name: eks
            kind: terraform
```

## Values Handling

Variables from the `vars` section are passed to Helmfile and available in templates:

```yaml
# In stack manifest
components:
  helmfile:
    my-app:
      vars:
        image_tag: "v1.2.3"
        replicas: 5
        resources:
          requests:
            cpu: "100m"
            memory: "256Mi"
```

```yaml
# In helmfile.yaml
releases:
  - name: my-app
    values:
      - image:
          tag: {{ .Values.image_tag | quote }}
        replicaCount: {{ .Values.replicas }}
        resources:
          requests:
            cpu: {{ .Values.resources.requests.cpu | quote }}
            memory: {{ .Values.resources.requests.memory | quote }}
```

## Best Practices

1. **Use Dependencies:** Define `dependencies.components` to ensure Helmfile components deploy after their infrastructure dependencies.

2. **Centralize Chart Versions:** Define chart versions in catalog defaults and override only when necessary.

3. **Use Atomic Deployments:** Set `atomic: true` to automatically roll back failed deployments.

4. **Manage Kubeconfig:** Use environment variables or settings to manage kubeconfig paths consistently.

5. **Version Pin Charts:** Always specify explicit chart versions for reproducible deployments.

6. **Separate Concerns:** Create separate Helmfile components for distinct services rather than combining everything into one.

## Related

- [Component Types Overview](/stacks/components)
- [Terraform Components](/stacks/components/terraform)
- [Configure Variables](/stacks/vars)
- [Configure Environment](/stacks/env)
- [Passing Secrets](/cli/configuration/secrets)
- [`!secret` function](/functions/yaml/secret)
- [Component Dependencies](/stacks/dependencies/components)
