1# Helmfile [![CircleCI](https://circleci.com/gh/roboll/helmfile.svg?style=svg)](https://circleci.com/gh/roboll/helmfile) 2 3Deploy Kubernetes Helm Charts 4 5[![Docker Repository on Quay](https://quay.io/repository/roboll/helmfile/status "Docker Repository on Quay")](https://quay.io/repository/roboll/helmfile) 6[![Slack Community #helmfile](https://slack.sweetops.com/badge.svg)](https://slack.sweetops.com) 7 8## Status 9 10Even though Helmfile is used in production environments [across multiple organizations](USERS.md), it is still in its early stage of development, hence versioned 0.x. 11 12Helmfile complies to Semantic Versioning 2.0.0 in which v0.x means that there could be backward-incompatible changes for every release. 13 14Note that we will try our best to document any backward incompatibility. And in reality, helmfile had no breaking change for a year or so. 15 16## About 17 18Helmfile is a declarative spec for deploying helm charts. It lets you... 19 20* Keep a directory of chart value files and maintain changes in version control. 21* Apply CI/CD to configuration changes. 22* Periodically sync to avoid skew in environments. 23 24To avoid upgrades for each iteration of `helm`, the `helmfile` executable delegates to `helm` - as a result, `helm` must be installed. 25 26## Highlights 27 28**Declarative**: Write, version-control, apply the desired state file for visibility and reproducibility. 29 30**Modules**: Modularize common patterns of your infrastructure, distribute it via Git, S3, etc. to be reused across the entire company (See [#648](https://github.com/roboll/helmfile/pull/648)) 31 32**Versatility**: Manage your cluster consisting of charts, [kustomizations](https://github.com/kubernetes-sigs/kustomize), and directories of Kubernetes resources, turning everything to Helm releases (See [#673](https://github.com/roboll/helmfile/pull/673)) 33 34**Patch**: JSON/Strategic-Merge Patch Kubernetes resources before `helm-install`ing, without forking upstream charts (See [#673](https://github.com/roboll/helmfile/pull/673)) 35 36## Configuration 37 38**CAUTION**: This documentation is for the development version of Helmfile. If you are looking for the documentation for any of releases, please switch to the corresponding release tag like [v0.92.1](https://github.com/roboll/helmfile/tree/v0.92.1). 39 40The default name for a helmfile is `helmfile.yaml`: 41 42```yaml 43# Chart repositories used from within this state file 44# 45# Use `helm-s3` and `helm-git` and whatever Helm Downloader plugins 46# to use repositories other than the official repository or one backend by chartmuseum. 47repositories: 48# To use official "stable" charts a.k.a https://github.com/helm/charts/tree/master/stable 49- name: stable 50 url: https://charts.helm.sh/stable 51# To use official "incubator" charts a.k.a https://github.com/helm/charts/tree/master/incubator 52- name: incubator 53 url: https://charts.helm.sh/incubator 54# helm-git powered repository: You can treat any Git repository as a charts repository 55- name: polaris 56 url: git+https://github.com/reactiveops/polaris@deploy/helm?ref=master 57# Advanced configuration: You can setup basic or tls auth and optionally enable helm OCI integration 58- name: roboll 59 url: http://roboll.io/charts 60 certFile: optional_client_cert 61 keyFile: optional_client_key 62 username: optional_username 63 password: optional_password 64 oci: true 65# Advanced configuration: You can use a ca bundle to use an https repo 66# with a self-signed certificate 67- name: insecure 68 url: https://charts.my-insecure-domain.com 69 caFile: optional_ca_crt 70 71# context: kube-context # this directive is deprecated, please consider using helmDefaults.kubeContext 72 73# Path to alternative helm binary (--helm-binary) 74helmBinary: path/to/helm3 75 76# Default values to set for args along with dedicated keys that can be set by contributors, cli args take precedence over these. 77# In other words, unset values results in no flags passed to helm. 78# See the helm usage (helm SUBCOMMAND -h) for more info on default values when those flags aren't provided. 79helmDefaults: 80 tillerNamespace: tiller-namespace #dedicated default key for tiller-namespace 81 tillerless: false #dedicated default key for tillerless 82 kubeContext: kube-context #dedicated default key for kube-context (--kube-context) 83 cleanupOnFail: false #dedicated default key for helm flag --cleanup-on-fail 84 # additional and global args passed to helm (default "") 85 args: 86 - "--set k=v" 87 # verify the chart before upgrading (only works with packaged charts not directories) (default false) 88 verify: true 89 # wait for k8s resources via --wait. (default false) 90 wait: true 91 # time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks, and waits on pod/pvc/svc/deployment readiness) (default 300) 92 timeout: 600 93 # performs pods restart for the resource if applicable (default false) 94 recreatePods: true 95 # forces resource update through delete/recreate if needed (default false) 96 force: false 97 # enable TLS for request to Tiller (default false) 98 tls: true 99 # path to TLS CA certificate file (default "$HELM_HOME/ca.pem") 100 tlsCACert: "path/to/ca.pem" 101 # path to TLS certificate file (default "$HELM_HOME/cert.pem") 102 tlsCert: "path/to/cert.pem" 103 # path to TLS key file (default "$HELM_HOME/key.pem") 104 tlsKey: "path/to/key.pem" 105 # limit the maximum number of revisions saved per release. Use 0 for no limit. (default 10) 106 historyMax: 10 107 # when using helm 3.2+, automatically create release namespaces if they do not exist (default true) 108 createNamespace: true 109 # if used with charts museum allows to pull unstable charts for deployment, for example: if 1.2.3 and 1.2.4-dev versions exist and set to true, 1.2.4-dev will be pulled (default false) 110 devel: true 111 # When set to `true`, skips running `helm dep up` and `helm dep build` on this release's chart. 112 # Useful when the chart is broken, like seen in https://github.com/roboll/helmfile/issues/1547 113 skipDeps: false 114 115# these labels will be applied to all releases in a Helmfile. Useful in templating if you have a helmfile per environment or customer and don't want to copy the same label to each release 116commonLabels: 117 hello: world 118 119# The desired states of Helm releases. 120# 121# Helmfile runs various helm commands to converge the current state in the live cluster to the desired state defined here. 122releases: 123 # Published chart example 124 - name: vault # name of this release 125 namespace: vault # target namespace 126 createNamespace: true # helm 3.2+ automatically create release namespace (default true) 127 labels: # Arbitrary key value pairs for filtering releases 128 foo: bar 129 chart: roboll/vault-secret-manager # the chart being installed to create this release, referenced by `repository/chart` syntax 130 version: ~1.24.1 # the semver of the chart. range constraint is supported 131 condition: vault.enabled # The values lookup key for filtering releases. Corresponds to the boolean value of `vault.enabled`, where `vault` is an arbitrary value 132 missingFileHandler: Warn # set to either "Error" or "Warn". "Error" instructs helmfile to fail when unable to find a values or secrets file. When "Warn", it prints the file and continues. 133 # Values files used for rendering the chart 134 values: 135 # Value files passed via --values 136 - vault.yaml 137 # Inline values, passed via a temporary values file and --values, so that it doesn't suffer from type issues like --set 138 - address: https://vault.example.com 139 # Go template available in inline values and values files. 140 - image: 141 # The end result is more or less YAML. So do `quote` to prevent number-like strings from accidentally parsed into numbers! 142 # See https://github.com/roboll/helmfile/issues/608 143 tag: {{ requiredEnv "IMAGE_TAG" | quote }} 144 # Otherwise: 145 # tag: "{{ requiredEnv "IMAGE_TAG" }}" 146 # tag: !!string {{ requiredEnv "IMAGE_TAG" }} 147 db: 148 username: {{ requiredEnv "DB_USERNAME" }} 149 # value taken from environment variable. Quotes are necessary. Will throw an error if the environment variable is not set. $DB_PASSWORD needs to be set in the calling environment ex: export DB_PASSWORD='password1' 150 password: {{ requiredEnv "DB_PASSWORD" }} 151 proxy: 152 # Interpolate environment variable with a fixed string 153 domain: {{ requiredEnv "PLATFORM_ID" }}.my-domain.com 154 scheme: {{ env "SCHEME" | default "https" }} 155 # Use `values` whenever possible! 156 # `set` translates to helm's `--set key=val`, that is known to suffer from type issues like https://github.com/roboll/helmfile/issues/608 157 set: 158 # single value loaded from a local file, translates to --set-file foo.config=path/to/file 159 - name: foo.config 160 file: path/to/file 161 # set a single array value in an array, translates to --set bar[0]={1,2} 162 - name: bar[0] 163 values: 164 - 1 165 - 2 166 # set a templated value 167 - name: namespace 168 value: {{ .Namespace }} 169 # will attempt to decrypt it using helm-secrets plugin 170 secrets: 171 - vault_secret.yaml 172 # Override helmDefaults options for verify, wait, timeout, recreatePods and force. 173 verify: true 174 wait: true 175 timeout: 60 176 recreatePods: true 177 force: false 178 # set `false` to uninstall this release on sync. (default true) 179 installed: true 180 # restores previous state in case of failed release (default false) 181 atomic: true 182 # when true, cleans up any new resources created during a failed release (default false) 183 cleanupOnFail: false 184 # name of the tiller namespace (default "") 185 tillerNamespace: vault 186 # if true, will use the helm-tiller plugin (default false) 187 tillerless: false 188 # enable TLS for request to Tiller (default false) 189 tls: true 190 # path to TLS CA certificate file (default "$HELM_HOME/ca.pem") 191 tlsCACert: "path/to/ca.pem" 192 # path to TLS certificate file (default "$HELM_HOME/cert.pem") 193 tlsCert: "path/to/cert.pem" 194 # path to TLS key file (default "$HELM_HOME/key.pem") 195 tlsKey: "path/to/key.pem" 196 # --kube-context to be passed to helm commands 197 # CAUTION: this doesn't work as expected for `tilerless: true`. 198 # See https://github.com/roboll/helmfile/issues/642 199 # (default "", which means the standard kubeconfig, either ~/kubeconfig or the file pointed by $KUBECONFIG environment variable) 200 kubeContext: kube-context 201 # passes --disable-validation to helm 3 diff plugin, this requires diff plugin >= 3.1.2 202 # It may be helpful to deploy charts with helm api v1 CRDS 203 # https://github.com/roboll/helmfile/pull/1373 204 disableValidation: false 205 # passes --disable-validation to helm 3 diff plugin, this requires diff plugin >= 3.1.2 206 # It is useful when any release contains custom resources for CRDs that is not yet installed onto the cluster. 207 # https://github.com/roboll/helmfile/pull/1618 208 disableValidationOnInstall: false 209 # passes --disable-openapi-validation to helm 3 diff plugin, this requires diff plugin >= 3.1.2 210 # It may be helpful to deploy charts with helm api v1 CRDS 211 # https://github.com/roboll/helmfile/pull/1373 212 disableOpenApiValidation: false 213 # limit the maximum number of revisions saved per release. Use 0 for no limit (default 10) 214 historyMax: 10 215 # When set to `true`, skips running `helm dep up` and `helm dep build` on this release's chart. 216 # Useful when the chart is broken, like seen in https://github.com/roboll/helmfile/issues/1547 217 skipDeps: false 218 219 # Local chart example 220 - name: grafana # name of this release 221 namespace: another # target namespace 222 chart: ../my-charts/grafana # the chart being installed to create this release, referenced by relative path to local helmfile 223 values: 224 - "../../my-values/grafana/values.yaml" # Values file (relative path to manifest) 225 - ./values/{{ requiredEnv "PLATFORM_ENV" }}/config.yaml # Values file taken from path with environment variable. $PLATFORM_ENV must be set in the calling environment. 226 wait: true 227 228# 229# Advanced Configuration: Nested States 230# 231helmfiles: 232- # Path to the helmfile state file being processed BEFORE releases in this state file 233 path: path/to/subhelmfile.yaml 234 # Label selector used for filtering releases in the nested state. 235 # For example, `name=prometheus` in this context is equivalent to processing the nested state like 236 # helmfile -f path/to/subhelmfile.yaml -l name=prometheus sync 237 selectors: 238 - name=prometheus 239 # Override state values 240 values: 241 # Values files merged into the nested state's values 242 - additional.values.yaml 243 # One important aspect of using values here is that they first need to be defined in the values section 244 # of the origin helmfile, so in this example key1 needs to be in the values or environments.NAME.values of path/to/subhelmfile.yaml 245 # Inline state values merged into the nested state's values 246 - key1: val1 247- # All the nested state files under `helmfiles:` is processed in the order of definition. 248 # So it can be used for preparation for your main `releases`. An example would be creating CRDs required by `releases` in the parent state file. 249 path: path/to/mycrd.helmfile.yaml 250- # Terraform-module-like URL for importing a remote directory and use a file in it as a nested-state file 251 # The nested-state file is locally checked-out along with the remote directory containing it. 252 # Therefore all the local paths in the file are resolved relative to the file 253 path: git::https://github.com/cloudposse/helmfiles.git@releases/kiam.yaml?ref=0.40.0 254# If set to "Error", return an error when a subhelmfile points to a 255# non-existent path. The default behavior is to print a warning and continue. 256missingFileHandler: Error 257 258# 259# Advanced Configuration: Environments 260# 261 262# The list of environments managed by helmfile. 263# 264# The default is `environments: {"default": {}}` which implies: 265# 266# - `{{ .Environment.Name }}` evaluates to "default" 267# - `{{ .Values }}` being empty 268environments: 269 # The "default" environment is available and used when `helmfile` is run without `--environment NAME`. 270 default: 271 # Everything from the values.yaml is available via `{{ .Values.KEY }}`. 272 # Suppose `{"foo": {"bar": 1}}` contained in the values.yaml below, 273 # `{{ .Values.foo.bar }}` is evaluated to `1`. 274 values: 275 - environments/default/values.yaml 276 # Each entry in values can be either a file path or inline values. 277 # The below is an example of inline values, which is merged to the `.Values` 278 - myChartVer: 1.0.0-dev 279 # Any environment other than `default` is used only when `helmfile` is run with `--environment NAME`. 280 # That is, the "production" env below is used when and only when it is run like `helmfile --environment production sync`. 281 production: 282 values: 283 - environment/production/values.yaml 284 - myChartVer: 1.0.0 285 # disable vault release processing 286 - vault: 287 enabled: false 288 ## `secrets.yaml` is decrypted by `helm-secrets` and available via `{{ .Environment.Values.KEY }}` 289 secrets: 290 - environment/production/secrets.yaml 291 # Instructs helmfile to fail when unable to find a environment values file listed under `environments.NAME.values`. 292 # 293 # Possible values are "Error", "Warn", "Info", "Debug". The default is "Error". 294 # 295 # Use "Warn", "Info", or "Debug" if you want helmfile to not fail when a values file is missing, while just leaving 296 # a message about the missing file at the log-level. 297 missingFileHandler: Error 298 299# 300# Advanced Configuration: Layering 301# 302# Helmfile merges all the "base" state files and this state file before processing. 303# 304# Assuming this state file is named `helmfile.yaml`, all the files are merged in the order of: 305# environments.yaml <- defaults.yaml <- templates.yaml <- helmfile.yaml 306bases: 307- environments.yaml 308- defaults.yaml 309- templates.yaml 310 311# 312# Advanced Configuration: API Capabilities 313# 314# 'helmfile template' renders releases locally without querying an actual cluster, 315# and in this case `.Capabilities.APIVersions` cannot be populated. 316# When a chart queries for a specific CRD, this can lead to unexpected results. 317# 318# Configure a fixed list of api versions to pass to 'helm template' via the --api-versions flag: 319apiVersions: 320- example/v1 321 322``` 323 324## Templating 325 326Helmfile uses [Go templates](https://godoc.org/text/template) for templating your helmfile.yaml. While go ships several built-in functions, we have added all of the functions in the [Sprig library](https://godoc.org/github.com/Masterminds/sprig). 327 328We also added the following functions: 329 330- `requiredEnv` 331- `exec` 332- `readFile` 333- `toYaml` 334- `fromYaml` 335- `setValueAtPath` 336- `get` (Sprig's original `get` is available as `sprigGet`) 337- `tpl` 338- `required` 339- `fetchSecretValue` 340- `expandSecretRefs` 341 342We also added one special template function: `requiredEnv`. 343The `requiredEnv` function allows you to declare a particular environment variable as required for template rendering. 344If the environment variable is unset or empty, the template rendering will fail with an error message. 345 346## Using environment variables 347 348Environment variables can be used in most places for templating the helmfile. Currently this is supported for `name`, `namespace`, `value` (in set), `values` and `url` (in repositories). 349 350Examples: 351 352```yaml 353repositories: 354- name: your-private-git-repo-hosted-charts 355 url: https://{{ requiredEnv "GITHUB_TOKEN"}}@raw.githubusercontent.com/kmzfs/helm-repo-in-github/master/ 356``` 357 358```yaml 359releases: 360 - name: {{ requiredEnv "NAME" }}-vault 361 namespace: {{ requiredEnv "NAME" }} 362 chart: roboll/vault-secret-manager 363 values: 364 - db: 365 username: {{ requiredEnv "DB_USERNAME" }} 366 password: {{ requiredEnv "DB_PASSWORD" }} 367 set: 368 - name: proxy.domain 369 value: {{ requiredEnv "PLATFORM_ID" }}.my-domain.com 370 - name: proxy.scheme 371 value: {{ env "SCHEME" | default "https" }} 372``` 373 374### Note 375 376If you wish to treat your enviroment variables as strings always, even if they are boolean or numeric values you can use `{{ env "ENV_NAME" | quote }}` or `"{{ env "ENV_NAME" }}"`. These approaches also work with `requiredEnv`. 377 378## Installation 379 380- download one of [releases](https://github.com/roboll/helmfile/releases) or 381- [run as a container](#running-as-a-container) or 382- Archlinux: install via `pacman -S helmfile` or from [AUR](https://aur.archlinux.org/packages/kubernetes-helmfile-bin/) or 383- openSUSE: install via `zypper in helmfile` assuming you are on Tumbleweed; if you are on Leap you must add the [kubic](https://download.opensuse.org/repositories/devel:/kubic/) repo for your distribution version once before that command, e.g. `zypper ar https://download.opensuse.org/repositories/devel:/kubic/openSUSE_Leap_\$releasever kubic`, or 384- Windows (using [scoop](https://scoop.sh/)): `scoop install helmfile` 385- macOS (using [homebrew](https://brew.sh/)): `brew install helmfile` 386 387### Running as a container 388 389The [Helmfile Docker images are available in Quay](https://quay.io/roboll/helmfile). There is no `latest` tag, since the `0.x` versions can contain breaking changes, so make sure you pick the right tag. Example using `helmfile 0.135.0`: 390 391```sh-session 392# helm 2 393$ docker run --rm --net=host -v "${HOME}/.kube:/root/.kube" -v "${HOME}/.helm:/root/.helm" -v "${PWD}:/wd" --workdir /wd quay.io/roboll/helmfile:v0.135.0 helmfile sync 394 395# helm 3 396$ docker run --rm --net=host -v "${HOME}/.kube:/root/.kube" -v "${HOME}/.config/helm:/root/.config/helm" -v "${PWD}:/wd" --workdir /wd quay.io/roboll/helmfile:helm3-v0.135.0 helmfile sync 397``` 398 399You can also use shims to make calling the binaries easier: 400 401```sh-session 402# helm 2 403$ printf '%s\n' '#!/bin/sh' 'docker run --rm --net=host -v "${HOME}/.kube:/root/.kube" -v "${HOME}/.helm:/root/.helm" -v "${PWD}:/wd" --workdir /wd quay.io/roboll/helmfile:v0.135.0 helmfile "$@"' | 404 tee helmfile 405$ chmod +x helmfile 406$ ./helmfile sync 407 408# helm 3 409$ printf '%s\n' '#!/bin/sh' 'docker run --rm --net=host -v "${HOME}/.kube:/root/.kube" -v "${HOME}/.config/helm:/root/.config/helm" -v "${PWD}:/wd" --workdir /wd quay.io/roboll/helmfile:helm3-v0.135.0 helmfile "$@"' | 410 tee helmfile 411$ chmod +x helmfile 412$ ./helmfile sync 413``` 414 415## Getting Started 416 417Let's start with a simple `helmfile` and gradually improve it to fit your use-case! 418 419Suppose the `helmfile.yaml` representing the desired state of your helm releases looks like: 420 421```yaml 422releases: 423- name: prom-norbac-ubuntu 424 namespace: prometheus 425 chart: stable/prometheus 426 set: 427 - name: rbac.create 428 value: false 429``` 430 431Sync your Kubernetes cluster state to the desired one by running: 432 433```console 434helmfile apply 435``` 436 437Congratulations! You now have your first Prometheus deployment running inside your cluster. 438 439Iterate on the `helmfile.yaml` by referencing: 440 441- [Configuration](#configuration) 442- [CLI reference](#cli-reference). 443- [Helmfile Best Practices Guide](https://github.com/roboll/helmfile/blob/master/docs/writing-helmfile.md) 444 445## CLI Reference 446 447``` 448NAME: 449 helmfile 450 451USAGE: 452 helmfile [global options] command [command options] [arguments...] 453 454VERSION: 455 v0.138.6 456 457COMMANDS: 458 deps update charts based on their requirements 459 repos sync repositories from state file (helm repo add && helm repo update) 460 charts DEPRECATED: sync releases from state file (helm upgrade --install) 461 diff diff releases from state file against env (helm diff) 462 template template releases from state file against env (helm template) 463 write-values write values files for releases. Similar to `helmfile template`, write values files instead of manifests. 464 lint lint charts from state file (helm lint) 465 sync sync all resources from state file (repos, releases and chart deps) 466 apply apply all resources from state file only when there are changes 467 status retrieve status of releases in state file 468 delete DEPRECATED: delete releases from state file (helm delete) 469 destroy deletes and then purges releases 470 test test releases from state file (helm test) 471 build output compiled helmfile state(s) as YAML 472 list list releases defined in state file 473 version Show the version for Helmfile. 474 help, h Shows a list of commands or help for one command 475 476GLOBAL OPTIONS: 477 --helm-binary value, -b value path to helm binary (default: "helm") 478 --file helmfile.yaml, -f helmfile.yaml load config from file or directory. defaults to helmfile.yaml or `helmfile.d`(means `helmfile.d/*.yaml`) in this preference 479 --environment value, -e value specify the environment name. defaults to "default" 480 --state-values-set value set state values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) 481 --state-values-file value specify state values in a YAML file 482 --quiet, -q Silence output. Equivalent to log-level warn 483 --kube-context value Set kubectl context. Uses current context by default 484 --debug Enable verbose output for Helm and set log-level to debug, this disables --quiet/-q effect 485 --no-color Output without color 486 --log-level value Set log level, default info 487 --namespace value, -n value Set namespace. Uses the namespace set in the context by default, and is available in templates as {{ .Namespace }} 488 --selector value, -l value Only run using the releases that match labels. Labels can take the form of foo=bar or foo!=bar. 489 A release must match all labels in a group in order to be used. Multiple groups can be specified at once. 490 --selector tier=frontend,tier!=proxy --selector tier=backend. Will match all frontend, non-proxy releases AND all backend releases. 491 The name of a release can be used as a label. --selector name=myrelease 492 --allow-no-matching-release Do not exit with an error code if the provided selector has no matching releases. 493 --interactive, -i Request confirmation before attempting to modify clusters 494 --help, -h show help 495 --version, -v print the version 496``` 497 498### sync 499 500The `helmfile sync` sub-command sync your cluster state as described in your `helmfile`. The default helmfile is `helmfile.yaml`, but any YAML file can be passed by specifying a `--file path/to/your/yaml/file` flag. 501 502Under the covers, Helmfile executes `helm upgrade --install` for each `release` declared in the manifest, by optionally decrypting [secrets](#secrets) to be consumed as helm chart values. It also updates specified chart repositories and updates the 503dependencies of any referenced local charts. 504 505For Helm 2.9+ you can use a username and password to authenticate to a remote repository. 506 507### deps 508 509The `helmfile deps` sub-command locks your helmfile state and local charts dependencies. 510 511It basically runs `helm dependency update` on your helmfile state file and all the referenced local charts, so that you get a "lock" file per each helmfile state or local chart. 512 513All the other `helmfile` sub-commands like `sync` use chart versions recorded in the lock files, so that e.g. untested chart versions won't suddenly get deployed to the production environment. 514 515For example, the lock file for a helmfile state file named `helmfile.1.yaml` will be `helmfile.1.lock`. The lock file for a local chart would be `requirements.lock`, which is the same as `helm`. 516 517It is recommended to version-control all the lock files, so that they can be used in the production deployment pipeline for extra reproducibility. 518 519To bring in chart updates systematically, it would also be a good idea to run `helmfile deps` regularly, test it, and then update the lock files in the version-control system. 520 521### diff 522 523The `helmfile diff` sub-command executes the [helm-diff](https://github.com/databus23/helm-diff) plugin across all of 524the charts/releases defined in the manifest. 525 526To supply the diff functionality Helmfile needs the [helm-diff](https://github.com/databus23/helm-diff) plugin v2.9.0+1 or greater installed. For Helm 2.3+ 527you should be able to simply execute `helm plugin install https://github.com/databus23/helm-diff`. For more details 528please look at their [documentation](https://github.com/databus23/helm-diff#helm-diff-plugin). 529 530### apply 531 532The `helmfile apply` sub-command begins by executing `diff`. If `diff` finds that there is any changes, `sync` is executed. Adding `--interactive` instructs Helmfile to request your confirmation before `sync`. 533 534An expected use-case of `apply` is to schedule it to run periodically, so that you can auto-fix skews between the desired and the current state of your apps running on Kubernetes clusters. 535 536### destroy 537 538The `helmfile destroy` sub-command uninstalls and purges all the releases defined in the manifests. 539 540`helmfile --interactive destroy` instructs Helmfile to request your confirmation before actually deleting releases. 541 542`destroy` basically runs `helm uninstall --purge` on all the targeted releases. If you don't want purging, use `helmfile delete` instead. 543 544### delete (DEPRECATED) 545 546The `helmfile delete` sub-command deletes all the releases defined in the manifests. 547 548`helmfile --interactive delete` instructs Helmfile to request your confirmation before actually deleting releases. 549 550Note that `delete` doesn't purge releases. So `helmfile delete && helmfile sync` results in sync failed due to that releases names are not deleted but preserved for future references. If you really want to remove releases for reuse, add `--purge` flag to run it like `helmfile delete --purge`. 551 552### secrets 553 554The `secrets` parameter in a `helmfile.yaml` causes the [helm-secrets](https://github.com/jkroepke/helm-secrets) plugin to be executed to decrypt the file. 555 556To supply the secret functionality Helmfile needs the `helm secrets` plugin installed. For Helm 2.3+ 557you should be able to simply execute `helm plugin install https://github.com/jkroepke/helm-secrets 558`. 559 560### test 561 562The `helmfile test` sub-command runs a `helm test` against specified releases in the manifest, default to all 563 564Use `--cleanup` to delete pods upon completion. 565 566### lint 567 568The `helmfile lint` sub-command runs a `helm lint` across all of the charts/releases defined in the manifest. Non local charts will be fetched into a temporary folder which will be deleted once the task is completed. 569 570## Paths Overview 571 572Using manifest files in conjunction with command line argument can be a bit confusing. 573 574A few rules to clear up this ambiguity: 575 576- Absolute paths are always resolved as absolute paths 577- Relative paths referenced *in* the Helmfile manifest itself are relative to that manifest 578- Relative paths referenced on the command line are relative to the current working directory the user is in 579 580For additional context, take a look at [paths examples](PATHS.md). 581 582## Labels Overview 583 584A selector can be used to only target a subset of releases when running Helmfile. This is useful for large helmfiles with releases that are logically grouped together. 585 586Labels are simple key value pairs that are an optional field of the release spec. When selecting by label, the search can be inverted. `tier!=backend` would match all releases that do NOT have the `tier: backend` label. `tier=fronted` would only match releases with the `tier: frontend` label. 587 588Multiple labels can be specified using `,` as a separator. A release must match all selectors in order to be selected for the final helm command. 589 590The `selector` parameter can be specified multiple times. Each parameter is resolved independently so a release that matches any parameter will be used. 591 592`--selector tier=frontend --selector tier=backend` will select all the charts. 593 594In addition to user supplied labels, the name, the namespace, and the chart are available to be used as selectors. The chart will just be the chart name excluding the repository (Example `stable/filebeat` would be selected using `--selector chart=filebeat`). 595 596`commonLabels` can be used when you want to apply the same label to all releases and use [templating](##Templates) based on that. 597For instance, you install a number of charts on every customer but need to provide different values file per customer. 598 599templates/common.yaml: 600 601```yaml 602templates: 603 nginx: &nginx 604 name: nginx 605 chart: stable/nginx-ingress 606 values: 607 - ../values/common/{{ .Release.Name }}.yaml 608 - ../values/{{ .Release.Labels.customer }}/{{ .Release.Name }}.yaml 609 610 cert-manager: &cert-manager 611 name: cert-manager 612 chart: jetstack/cert-manager 613 values: 614 - ../values/common/{{ .Release.Name }}.yaml 615 - ../values/{{ .Release.Labels.customer }}/{{ .Release.Name }}.yaml 616``` 617 618helmfile.yaml: 619 620```yaml 621{{ readFile "templates/common.yaml" }} 622 623commonLabels: 624 customer: company 625 626releases: 627- <<: *nginx 628- <<: *cert-manager 629``` 630 631## Templates 632 633You can use go's text/template expressions in `helmfile.yaml` and `values.yaml.gotmpl` (templated helm values files). `values.yaml` references will be used verbatim. In other words: 634 635- for value files ending with `.gotmpl`, template expressions will be rendered 636- for plain value files (ending in `.yaml`), content will be used as-is 637 638In addition to built-in ones, the following custom template functions are available: 639 640- `readFile` reads the specified local file and generate a golang string 641- `fromYaml` reads a golang string and generates a map 642- `setValueAtPath PATH NEW_VALUE` traverses a golang map, replaces the value at the PATH with NEW_VALUE 643- `toYaml` marshals a map into a string 644- `get` returns the value of the specified key if present in the `.Values` object, otherwise will return the default value defined in the function 645 646### Values Files Templates 647 648You can reference a template of values file in your `helmfile.yaml` like below: 649 650```yaml 651releases: 652- name: myapp 653 chart: mychart 654 values: 655 - values.yaml.gotmpl 656``` 657 658Every values file whose file extension is `.gotmpl` is considered as a template file. 659 660Suppose `values.yaml.gotmpl` was something like: 661 662```yaml 663{{ readFile "values.yaml" | fromYaml | setValueAtPath "foo.bar" "FOO_BAR" | toYaml }} 664``` 665 666And `values.yaml` was: 667 668```yaml 669foo: 670 bar: "" 671``` 672 673The resulting, temporary values.yaml that is generated from `values.yaml.gotmpl` would become: 674 675```yaml 676foo: 677 # Notice `setValueAtPath "foo.bar" "FOO_BAR"` in the template above 678 bar: FOO_BAR 679``` 680 681## Refactoring `helmfile.yaml` with values files templates 682 683One of expected use-cases of values files templates is to keep `helmfile.yaml` small and concise. 684 685See the example `helmfile.yaml` below: 686 687```yaml 688releases: 689 - name: {{ requiredEnv "NAME" }}-vault 690 namespace: {{ requiredEnv "NAME" }} 691 chart: roboll/vault-secret-manager 692 values: 693 - db: 694 username: {{ requiredEnv "DB_USERNAME" }} 695 password: {{ requiredEnv "DB_PASSWORD" }} 696 set: 697 - name: proxy.domain 698 value: {{ requiredEnv "PLATFORM_ID" }}.my-domain.com 699 - name: proxy.scheme 700 value: {{ env "SCHEME" | default "https" }} 701``` 702 703The `values` and `set` sections of the config file can be separated out into a template: 704 705`helmfile.yaml`: 706 707```yaml 708releases: 709 - name: {{ requiredEnv "NAME" }}-vault 710 namespace: {{ requiredEnv "NAME" }} 711 chart: roboll/vault-secret-manager 712 values: 713 - values.yaml.gotmpl 714``` 715 716`values.yaml.gotmpl`: 717 718```yaml 719db: 720 username: {{ requiredEnv "DB_USERNAME" }} 721 password: {{ requiredEnv "DB_PASSWORD" }} 722proxy: 723 domain: {{ requiredEnv "PLATFORM_ID" }}.my-domain.com 724 scheme: {{ env "SCHEME" | default "https" }} 725``` 726 727## Environment 728 729When you want to customize the contents of `helmfile.yaml` or `values.yaml` files per environment, use this feature. 730 731You can define as many environments as you want under `environments` in `helmfile.yaml`. 732 733The environment name defaults to `default`, that is, `helmfile sync` implies the `default` environment. 734The selected environment name can be referenced from `helmfile.yaml` and `values.yaml.gotmpl` by `{{ .Environment.Name }}`. 735 736If you want to specify a non-default environment, provide a `--environment NAME` flag to `helmfile` like `helmfile --environment production sync`. 737 738The below example shows how to define a production-only release: 739 740```yaml 741environments: 742 default: 743 production: 744 745releases: 746- name: newrelic-agent 747 installed: {{ eq .Environment.Name "production" | toYaml }} 748 # snip 749- name: myapp 750 # snip 751``` 752 753## Environment Values 754 755Environment Values allows you to inject a set of values specific to the selected environment, into values.yaml templates. 756Use it to inject common values from the environment to multiple values files, to make your configuration DRY. 757 758Suppose you have three files `helmfile.yaml`, `production.yaml` and `values.yaml.gotmpl`: 759 760`helmfile.yaml` 761 762```yaml 763environments: 764 production: 765 values: 766 - production.yaml 767 768releases: 769- name: myapp 770 values: 771 - values.yaml.gotmpl 772``` 773 774`production.yaml` 775 776```yaml 777domain: prod.example.com 778releaseName: prod 779``` 780 781`values.yaml.gotmpl` 782 783```yaml 784domain: {{ .Values | get "domain" "dev.example.com" }} 785``` 786 787`helmfile sync` installs `myapp` with the value `domain=dev.example.com`, 788whereas `helmfile --environment production sync` installs the app with the value `domain=prod.example.com`. 789 790For even more flexibility, you can now use values declared in the `environments:` section in other parts of your helmfiles: 791 792consider: 793`default.yaml` 794 795```yaml 796domain: dev.example.com 797releaseName: dev 798``` 799 800```yaml 801environments: 802 default: 803 values: 804 - default.yaml 805 production: 806 values: 807 - production.yaml # bare .yaml file, content will be used verbatim 808 - other.yaml.gotmpl # template directives with potential side-effects like `exec` and `readFile` will be honoured 809 810releases: 811- name: myapp-{{ .Values.releaseName }} # release name will be one of `dev` or `prod` depending on selected environment 812 values: 813 - values.yaml.gotmpl 814- name: production-specific-release 815 # this release would be installed only if selected environment is `production` 816 installed: {{ eq .Values.releaseName "prod" | toYaml }} 817 ... 818``` 819 820### Note on Environment.Values vs Values 821 822The `{{ .Values.foo }}` syntax is the recommended way of using environment values. 823 824Prior to this [pull request](https://github.com/roboll/helmfile/pull/647), environment values were made available through the `{{ .Environment.Values.foo }}` syntax. 825This is still working but is **deprecated** and the new `{{ .Values.foo }}` syntax should be used instead. 826 827You can read more infos about the feature proposal [here](https://github.com/roboll/helmfile/issues/640). 828 829### Loading remote environment values files 830 831Since #1296 and Helmfile v0.118.8, you can use `go-getter`-style URLs to refer to remote values files: 832 833```yaml 834environments: 835 cluster-azure-us-west: 836 values: 837 - git::https://git.company.org/helmfiles/global/azure.yaml?ref=master 838 - git::https://git.company.org/helmfiles/global/us-west.yaml?ref=master 839 cluster-gcp-europe-west: 840 values: 841 - git::https://git.company.org/helmfiles/global/gcp.yaml?ref=master 842 - git::https://git.company.org/helmfiles/global/europe-west.yaml?ref=master 843 844releases: 845 - ... 846``` 847 848This is particularly useful when you co-locate helmfiles within your project repo but want to reuse the definitions in a global repo. 849 850## Environment Secrets 851 852Environment Secrets (not to be confused with Kubernetes Secrets) are encrypted versions of `Environment Values`. 853You can list any number of `secrets.yaml` files created using `helm secrets` or `sops`, so that 854Helmfile could automatically decrypt and merge the secrets into the environment values. 855 856First you must have the [helm-secrets](https://github.com/jkroepke/helm-secrets) plugin installed along with a 857`.sops.yaml` file to configure the method of encryption (this can be in the same directory as your helmfile or 858in the sub-directory containing your secrets files). 859 860Then suppose you have a a foo.bar secret defined in `environments/production/secrets.yaml`: 861```yaml 862foo.bar: "mysupersecretstring" 863``` 864 865You can then encrypt it with `helm secrets enc environments/production/secrets.yaml` 866 867Then reference that encrypted file in `helmfile.yaml`: 868```yaml 869environments: 870 production: 871 secrets: 872 - environments/production/secrets.yaml 873 874releases: 875- name: myapp 876 chart: mychart 877 values: 878 - values.yaml.gotmpl 879``` 880 881Then the environment secret `foo.bar` can be referenced by the below template expression in your `values.yaml.gotmpl`: 882 883```yaml 884{{ .Values.foo.bar }} 885``` 886 887## Tillerless 888 889With the [helm-tiller](https://github.com/rimusz/helm-tiller) plugin installed, you can work without tiller installed. 890 891To enable this mode, you need to define `tillerless: true` and set the `tillerNamespace` in the `helmDefaults` section 892or in the `releases` entries. 893 894## DAG-aware installation/deletion ordering 895 896`needs` controls the order of the installation/deletion of the release: 897 898```yaml 899releases: 900- name: somerelease 901 needs: 902 - [TILLER_NAMESPACE/][NAMESPACE/]anotherelease 903``` 904 905Be aware that you have to specify the namespace name if you configured one for the release(s). 906 907All the releases listed under `needs` are installed before(or deleted after) the release itself. 908 909For the following example, `helmfile [sync|apply]` installs releases in this order: 910 9111. logging 9122. servicemesh 9133. myapp1 and myapp2 914 915```yaml 916 - name: myapp1 917 chart: charts/myapp 918 needs: 919 - servicemesh 920 - logging 921 - name: myapp2 922 chart: charts/myapp 923 needs: 924 - servicemesh 925 - logging 926 - name: servicemesh 927 chart: charts/istio 928 needs: 929 - logging 930 - name: logging 931 chart: charts/fluentd 932``` 933 934Note that all the releases in a same group is installed concurrently. That is, myapp1 and myapp2 are installed concurrently. 935 936On `helmfile [delete|destroy]`, deletions happen in the reverse order. 937 938That is, `myapp1` and `myapp2` are deleted first, then `servicemesh`, and finally `logging`. 939 940## Separating helmfile.yaml into multiple independent files 941 942Once your `helmfile.yaml` got to contain too many releases, 943split it into multiple yaml files. 944 945Recommended granularity of helmfile.yaml files is "per microservice" or "per team". 946And there are two ways to organize your files. 947 948- Single directory 949- Glob patterns 950 951### Single directory 952 953`helmfile -f path/to/directory` loads and runs all the yaml files under the specified directory, each file as an independent helmfile.yaml. 954The default helmfile directory is `helmfile.d`, that is, 955in case helmfile is unable to locate `helmfile.yaml`, it tries to locate `helmfile.d/*.yaml`. 956 957All the yaml files under the specified directory are processed in the alphabetical order. For example, you can use a `<two digit number>-<microservice>.yaml` naming convention to control the sync order. 958 959- `helmfile.d`/ 960 - `00-database.yaml` 961 - `00-backend.yaml` 962 - `01-frontend.yaml` 963 964### Glob patterns 965 966In case you want more control over how multiple `helmfile.yaml` files are organized, use `helmfiles:` configuration key in the `helmfile.yaml`: 967 968Suppose you have multiple microservices organized in a Git repository that looks like: 969 970- `myteam/` (sometimes it is equivalent to a k8s ns, that is `kube-system` for `clusterops` team) 971 - `apps/` 972 - `filebeat/` 973 - `helmfile.yaml` (no `charts/` exists, because it depends on the stable/filebeat chart hosted on the official helm charts repository) 974 - `README.md` (each app managed by my team has a dedicated README maintained by the owners of the app) 975 - `metricbeat/` 976 - `helmfile.yaml` 977 - `README.md` 978 - `elastalert-operator/` 979 - `helmfile.yaml` 980 - `README.md` 981 - `charts/` 982 - `elastalert-operator/` 983 - `<the content of the local helm chart>` 984 985The benefits of this structure is that you can run `git diff` to locate in which directory=microservice a git commit has changes. 986It allows your CI system to run a workflow for the changed microservice only. 987 988A downside of this is that you don't have an obvious way to sync all microservices at once. That is, you have to run: 989 990```bash 991for d in apps/*; do helmfile -f $d diff; if [ $? -eq 2 ]; then helmfile -f $d sync; fi; done 992``` 993 994At this point, you'll start writing a `Makefile` under `myteam/` so that `make sync-all` will do the job. 995 996It does work, but you can rely on the Helmfile feature instead. 997 998Put `myteam/helmfile.yaml` that looks like: 999 1000```yaml 1001helmfiles: 1002- apps/*/helmfile.yaml 1003``` 1004 1005So that you can get rid of the `Makefile` and the bash snippet. 1006Just run `helmfile sync` inside `myteam/`, and you are done. 1007 1008All the files are sorted alphabetically per group = array item inside `helmfiles:`, so that you have granular control over ordering, too. 1009 1010#### selectors 1011 1012When composing helmfiles you can use selectors from the command line as well as explicit selectors inside the parent helmfile to filter the releases to be used. 1013 1014```yaml 1015helmfiles: 1016- apps/*/helmfile.yaml 1017- path: apps/a-helmfile.yaml 1018 selectors: # list of selectors 1019 - name=prometheus 1020 - tier=frontend 1021- path: apps/b-helmfile.yaml # no selector, so all releases are used 1022selectors: [] 1023- path: apps/c-helmfile.yaml # parent selector to be used or cli selector for the initial helmfile 1024 selectorsInherited: true 1025``` 1026 1027* When a selector is specified, only this selector applies and the parents or CLI selectors are ignored. 1028* When not selector is specified there are 2 modes for the selector inheritance because we would like to change the current inheritance behavior (see [issue #344](https://github.com/roboll/helmfile/issues/344) ). 1029 * Legacy mode, sub-helmfiles without selectors inherit selectors from their parent helmfile. The initial helmfiles inherit from the command line selectors. 1030 * explicit mode, sub-helmfile without selectors do not inherit from their parent or the CLI selector. If you want them to inherit from their parent selector then use `selectorsInherited: true`. To enable this explicit mode you need to set the following environment variable `HELMFILE_EXPERIMENTAL=explicit-selector-inheritance` (see [experimental](#experimental-features)). 1031* Using `selector: []` will select all releases regardless of the parent selector or cli for the initial helmfile 1032* using `selectorsInherited: true` make the sub-helmfile selects releases with the parent selector or the cli for the initial helmfile. You cannot specify an explicit selector while using `selectorsInherited: true` 1033 1034## Importing values from any source 1035 1036The `exec` template function that is available in `values.yaml.gotmpl` is useful for importing values from any source 1037that is accessible by running a command: 1038 1039A usual usage of `exec` would look like this: 1040 1041```yaml 1042mysetting: | 1043{{ exec "./mycmd" (list "arg1" "arg2" "--flag1") | indent 2 }} 1044``` 1045 1046Or even with a pipeline: 1047 1048```yaml 1049mysetting: | 1050{{ yourinput | exec "./mycmd-consume-stdin" (list "arg1" "arg2") | indent 2 }} 1051``` 1052 1053The possibility is endless. Try importing values from your golang app, bash script, jsonnet, or anything! 1054 1055## Hooks 1056 1057A Helmfile hook is a per-release extension point that is composed of: 1058 1059- `events` 1060- `command` 1061- `args` 1062- `showlogs` 1063 1064Helmfile triggers various `events` while it is running. 1065Once `events` are triggered, associated `hooks` are executed, by running the `command` with `args`. The standard output of the `command` will be displayed if `showlogs` is set and it's value is `true`. 1066 1067Currently supported `events` are: 1068 1069- `prepare` 1070- `presync` 1071- `preuninstall` 1072- `postuninstall` 1073- `postsync` 1074- `cleanup` 1075 1076Hooks associated to `prepare` events are triggered after each release in your helmfile is loaded from YAML, before execution. 1077`prepare` hooks are triggered on the release as long as it is not excluded by the helmfile selector(e.g. `helmfile -l key=value`). 1078 1079Hooks associated to `presync` events are triggered before each release is applied to the remote cluster. 1080This is the ideal event to execute any commands that may mutate the cluster state as it will not be run for read-only operations like `lint`, `diff` or `template`. 1081 1082`preuninstall` hooks are triggered immediately before a release is uninstalled as part of `helmfile apply`, `helmfile sync`, `helmfile delete`, and `helmfile destroy`. 1083 1084`postuninstall` hooks are triggered immediately after successful uninstall of a release while running `helmfile apply`, `helmfile sync`, `helmfile delete`, `helmfile destroy`. 1085 1086`postsync` hooks are triggered after each release is synced(installed, updated, or uninstalled) to/from the cluster, regardless of the sync was successful or not. 1087This is the ideal place to execute any commands that may mutate the cluster state as it will not be run for read-only operations like `lint`, `diff` or `template`. 1088 1089`cleanup` hooks are triggered after each release is processed. 1090This is the counterpart to `prepare`, as any release on which `prepare` has been triggered gets `cleanup` triggered as well. 1091 1092The following is an example hook that just prints the contextual information provided to hook: 1093 1094```yaml 1095releases: 1096- name: myapp 1097 chart: mychart 1098 # *snip* 1099 hooks: 1100 - events: ["prepare", "cleanup"] 1101 showlogs: true 1102 command: "echo" 1103 args: ["{{`{{.Environment.Name}}`}}", "{{`{{.Release.Name}}`}}", "{{`{{.HelmfileCommand}}`}}\ 1104"] 1105``` 1106 1107Let's say you ran `helmfile --environment prod sync`, the above hook results in executing: 1108 1109``` 1110echo {{Environment.Name}} {{.Release.Name}} {{.HelmfileCommand}} 1111``` 1112 1113Whereas the template expressions are executed thus the command becomes: 1114 1115``` 1116echo prod myapp sync 1117``` 1118 1119Now, replace `echo` with any command you like, and rewrite `args` that actually conforms to the command, so that you can integrate any command that does: 1120 1121- templating 1122- linting 1123- testing 1124 1125For templating, imagine that you created a hook that generates a helm chart on-the-fly by running an external tool like ksonnet, kustomize, or your own template engine. 1126It will allow you to write your helm releases with any language you like, while still leveraging goodies provided by helm. 1127 1128### Global Hooks 1129 1130In contrast to the per release hooks mentioned above these are run only once at the very beginning and end of the execution of a helmfile command and only the `prepare` and `cleanup` hooks are available respectively. 1131 1132They use the same syntax as per release hooks, but at the top level of your helmfile: 1133```yaml 1134hooks: 1135- events: ["prepare", "cleanup"] 1136 showlogs: true 1137 command: "echo" 1138 args: ["{{`{{.Environment.Name}}`}}", "{{`{{.HelmfileCommand}}`}}\ 1139"] 1140``` 1141 1142### Helmfile + Kustomize 1143 1144Do you prefer `kustomize` to write and organize your Kubernetes apps, but still want to leverage helm's useful features 1145like rollback, history, and so on? This section is for you! 1146 1147The combination of `hooks` and [helmify-kustomize](https://gist.github.com/mumoshu/f9d0bd98e0eb77f636f79fc2fb130690) 1148enables you to integrate [kustomize](https://github.com/kubernetes-sigs/kustomize) into Helmfile. 1149 1150That is, you can use `kustomize` to build a local helm chart from a kustomize overlay. 1151 1152Let's assume you have a kustomize project named `foo-kustomize` like this: 1153 1154``` 1155foo-kustomize/ 1156├── base 1157│ ├── configMap.yaml 1158│ ├── deployment.yaml 1159│ ├── kustomization.yaml 1160│ └── service.yaml 1161└── overlays 1162 ├── default 1163 │ ├── kustomization.yaml 1164 │ └── map.yaml 1165 ├── production 1166 │ ├── deployment.yaml 1167 │ └── kustomization.yaml 1168 └── staging 1169 ├── kustomization.yaml 1170 └── map.yaml 1171 11725 directories, 10 files 1173``` 1174 1175Write `helmfile.yaml`: 1176 1177```yaml 1178- name: kustomize 1179 chart: ./foo 1180 hooks: 1181 - events: ["prepare", "cleanup"] 1182 command: "../helmify" 1183 args: ["{{`{{if eq .Event.Name \"prepare\"}}build{{else}}clean{{end}}`}}", "{{`{{.Release.Ch\ 1184art}}`}}", "{{`{{.Environment.Name}}`}}"] 1185``` 1186 1187Run `helmfile --environment staging sync` and see it results in helmfile running `kustomize build foo-kustomize/overlays/staging > foo/templates/all.yaml`. 1188 1189Voilà! You can mix helm releases that are backed by remote charts, local charts, and even kustomize overlays. 1190 1191## Guides 1192 1193Use the [Helmfile Best Practices Guide](/docs/writing-helmfile.md) to write advanced helmfiles that feature: 1194 1195- Default values 1196- Layering 1197 1198We also have dedicated documentation on the following topics which might interest you: 1199 1200- [Shared Configurations Across Teams](/docs/shared-configuration-across-teams.md) 1201 1202Or join our friendly slack community in the [`#helmfile`](https://slack.sweetops.com) channel to ask questions and get help. Check out our [slack archive](https://archive.sweetops.com/helmfile/) for good examples of how others are using it. 1203 1204## Using .env files 1205 1206Helmfile itself doesn't have an ability to load .env files. But you can write some bash script to achieve the goal: 1207 1208```console 1209set -a; . .env; set +a; helmfile sync 1210``` 1211 1212Please see #203 for more context. 1213 1214## Running Helmfile interactively 1215 1216`helmfile --interactive [apply|destroy]` requests confirmation from you before actually modifying your cluster. 1217 1218Use it when you're running `helmfile` manually on your local machine or a kind of secure administrative hosts. 1219 1220For your local use-case, aliasing it like `alias hi='helmfile --interactive'` would be convenient. 1221 1222## Running Helmfile without an Internet connection 1223 1224Once you download all required charts into your machine, you can run `helmfile charts` to deploy your apps. 1225It basically run only `helm upgrade --install` with your already-downloaded charts, hence no Internet connection is required. 1226See #155 for more information on this topic. 1227 1228## Experimental Features 1229 1230Some experimental features may be available for testing in perspective of being (or not) included in a future release. 1231Those features are set using the environment variable `HELMFILE_EXPERIMENTAL`. Here is the current experimental feature : 1232* `explicit-selector-inheritance` : remove today implicit cli selectors inheritance for composed helmfiles, see [composition selector](#selectors) 1233 1234If you want to enable all experimental features set the env var to `HELMFILE_EXPERIMENTAL=true` 1235 1236## `bash` and `zsh` completion 1237 1238Copy `autocomplete/helmfile_bash_autocomplete` or `autocomplete/helmfile_zsh_autocomplete` (depending on your shell of choice) to directory where you keep other shell completion scripts to make sure it is sourced. 1239 1240## Examples 1241 1242For more examples, see the [examples/README.md](https://github.com/roboll/helmfile/blob/master/examples/README.md) or the [`helmfile`](https://github.com/cloudposse/helmfiles/tree/master/releases) distribution by [Cloud Posse](https://github.com/cloudposse/). 1243 1244## Integrations 1245 1246- [renovate](https://github.com/renovatebot/renovate) automates chart version updates. See [this PR for more information](https://github.com/renovatebot/renovate/pull/5257). 1247 - For updating container image tags and git tags embedded within helmfile.yaml and values, you can use [renovate's regexManager](https://docs.renovatebot.com/modules/manager/regex/). Please see [this comment in the renovate repository](https://github.com/renovatebot/renovate/issues/6130#issuecomment-624061289) for more information. 1248- [ArgoCD Integration](#argocd-integration) 1249- [Azure ACR Integration](#azure-acr-integration) 1250 1251### ArgoCD Integration 1252 1253Use [ArgoCD](https://argoproj.github.io/argo-cd/) with `helmfile template` for GitOps. 1254 1255ArgoCD has support for kustomize/manifests/helm chart by itself. Why bother with Helmfile? 1256 1257The reasons may vary: 1258 12591. You do want to manage applications with ArgoCD, while letting Helmfile manage infrastructure-related components like Calico/Cilium/WeaveNet, Linkerd/Istio, and ArgoCD itself. 1260 - This way, any application deployed by ArgoCD has access to all the infrastructure. 1261 - Of course, you can use ArgoCD's [Sync Waves and Phases](https://argoproj.github.io/argo-cd/user-guide/sync-waves/) for ordering the infrastructure and application installations. But it may be difficult to separate the concern between the infrastructure and apps and annotate K8s resources consistently when you have different teams for managing infra and apps. 12622. You want to review the exact K8s manifests being applied on pull-request time, before ArgoCD syncs. 1263 - This is often better than using a kind of `HelmRelease` custom resources that obfuscates exactly what manifests are being applied, which makes reviewing harder. 12643. Use Helmfile as the single-pane of glass for all the K8s resources deployed to your cluster(s). 1265 - Helmfile can reduce repetition in K8s manifests across ArgoCD application 1266 1267For 1, you run `helmfile apply` on CI to deploy ArgoCD and the infrastructure components. 1268 1269> helmfile config for this phase often reside within the same directory as your Terraform project. So connecting the two with [terraform-provider-helmfile](https://github.com/mumoshu/terraform-provider-helmfile) may be helpful 1270 1271For 2, another app-centric CI or bot should render/commit manifests by running: 1272 1273``` 1274helmfile template --output-dir-template $(pwd)/gitops//{{.Release.Name}} 1275cd gitops 1276git add . 1277git commit -m 'some message' 1278git push origin $BRANCH 1279``` 1280 1281> Note that `$(pwd)` is necessary when `hemlfile.yaml` has one or more sub-helmfiles in nested directories, 1282> because setting a relative file path in `--output-dir` or `--output-dir-template` results in each sub-helmfile render 1283> to the directory relative to the specified path. 1284 1285so that they can be deployed by Argo CD as usual. 1286 1287 1288The CI or bot can optionally submit a PR to be review by human, running: 1289 1290``` 1291hub pull-request -b main -l gitops -m 'some description' 1292``` 1293 1294Recommendations: 1295 1296- Do create ArgoCD `Application` custom resource per Helm/Helmfile release, each point to respective sub-directory generated by `helmfile template --output-dir-template` 1297- If you don't directly push it to the main Git branch and instead go through a pull-request, do lint rendered manifests on your CI, so that you can catch easy mistakes earlier/before ArgoCD finally deploys it 1298- See [this ArgoCD issue](https://github.com/argoproj/argo-cd/issues/2143#issuecomment-570478329) for why you may want this, and see [this helmfile issue](https://github.com/roboll/helmfile/pull/1357) for how `--output-dir-template` works. 1299 1300### Azure ACR Integration 1301 1302Azure offers helm repository [support for Azure Container Registry](https://docs.microsoft.com/en-us/azure/container-registry/container-registry-helm-repos) as a preview feature. 1303 1304To use this you must first `az login` and then `az acr helm repo add -n <MyRegistry>`. This will extract a token for the given ACR and configure `helm` to use it, e.g. `helm repo update` should work straight away. 1305 1306To use `helmfile` with ACR, on the other hand, you must either include a username/password in the repository definition for the ACR in your `helmfile.yaml` or use the `--skip-deps` switch, e.g. `helmfile template --skip-deps`. 1307 1308An ACR repository definition in `helmfile.yaml` looks like this: 1309 1310```yaml 1311repositories: 1312 - name: <MyRegistry> 1313 url: https://<MyRegistry>.azurecr.io/helm/v1/repo 1314``` 1315 1316## OCI Registries 1317 1318In order to use OCI chart registries firstly they must be marked in the repository list as OCI enabled, e.g. 1319 1320```yaml 1321repositories: 1322 - name: myOCIRegistry 1323 url: myregistry.azurecr.io 1324 oci: true 1325``` 1326 1327It is important not to include a scheme for the URL as helm requires that these are not present for OCI registries 1328 1329Secondly the credentials for the OCI registry can either be specified within `helmfile.yaml` similar to 1330 1331```yaml 1332repositories: 1333 - name: myOCIRegistry 1334 url: myregistry.azurecr.io 1335 oci: true 1336 username: spongebob 1337 password: squarepants 1338``` 1339 1340or for CI scenarios these can be sourced from the environment with the format `<registryName>_USERNAME` and `<registryName_PASSWORD>`, e.g. 1341 1342```shell 1343export MYOCIREGISTRY_USERNAME=spongebob 1344export MYOCIREGISTRY_PASSWORD=squarepants 1345``` 1346 1347## Attribution 1348 1349We use: 1350 1351- [semtag](https://github.com/pnikosis/semtag) for automated semver tagging. I greatly appreciate the author(pnikosis)'s effort on creating it and their kindness to share it! 1352