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