# Environments ## Environment When you want to customize the contents of `helmfile.yaml` or `values.yaml` files per environment, use this feature. You can define as many environments as you want under `environments` in `helmfile.yaml`. `environments` section should be separated from `releases` with `---`. The environment name defaults to `default`, that is, `helmfile sync` implies the `default` environment. The selected environment name can be referenced from `helmfile.yaml` and `values.yaml.gotmpl` by `{{ .Environment.Name }}`. If you want to specify a non-default environment, provide a `--environment NAME` flag to `helmfile` like `helmfile --environment production sync`. The below example shows how to define a production-only release: ```yaml environments: default: production: --- releases: - name: newrelic-agent installed: {{ eq .Environment.Name "production" | toYaml }} # snip - name: myapp # snip ``` ### Environment Values Helmfile supports 3 values languages : - Straight yaml - Go templates to generate straight yaml - HCL Environment Values allows you to inject a set of values specific to the selected environment, into `values.yaml` templates. Use it to inject common values from the environment to multiple values files, to make your configuration DRY. Suppose you have three files `helmfile.yaml`, `production.yaml` and `values.yaml.gotmpl`: `helmfile.yaml` ```yaml environments: production: values: - production.yaml --- releases: - name: myapp values: - values.yaml.gotmpl ``` `production.yaml` ```yaml domain: prod.example.com releaseName: prod ``` `values.yaml.gotmpl` ```yaml domain: {{ .Values | get "domain" "dev.example.com" }} ``` `helmfile sync` installs `myapp` with the value `domain=dev.example.com`, whereas `helmfile --environment production sync` installs the app with the value `domain=prod.example.com`. For even more flexibility, you can now use values declared in the `environments:` section in other parts of your helmfiles: consider: `default.yaml` ```yaml domain: dev.example.com releaseName: dev ``` ```yaml environments: default: values: - default.yaml production: values: - production.yaml # bare .yaml file, content will be used verbatim - other.yaml.gotmpl # template directives with potential side-effects like `exec` and `readFile` will be honoured --- releases: - name: myapp-{{ .Values.releaseName }} # release name will be one of `dev` or `prod` depending on selected environment values: - values.yaml.gotmpl - name: production-specific-release # this release would be installed only if selected environment is `production` installed: {{ eq .Values.releaseName "prod" | toYaml }} ... ``` #### Merge strategy By default, when several files are listed under `values:`, later files override earlier files. Set `mergeStrategy: fallback` on the environment to flip the precedence so earlier files win and later files only fill missing keys: ```yaml environments: production: mergeStrategy: fallback values: - cluster-specific.yaml # wins on every key it defines - shared-defaults.yaml.gotmpl ``` Under `fallback`, explicit non-nil values in the earlier file (including zero values like `false`, `0`, `""`, and empty list) are preserved against any later file, while maps are deep-merged so later files may still add nested keys. A later `.gotmpl` file can also reference values from earlier files via `.Values`. See [Merge Strategy: override vs fallback](values-and-merging.md#4a-merge-strategy-override-vs-fallback) for the full semantics, including how explicit `null` is handled. #### HCL specifications Since Helmfile v0.164.0, HCL language is supported for environment values only. HCL values supports interpolations and sharing values across files * Only `.hcl` suffixed files will be interpreted as is * Helmfile supports 2 different blocks: `values` and `locals` * `values` block is a shared block where all values are accessible everywhere in all loaded files * `locals` block can't reference external values apart from the ones in the block itself, and where its defined values are only accessible in its local file * Only values in `values` blocks are made available to the final root `.Values` (e.g : ` values { myvar = "var" }` is accessed through `{{ .Values.myvar }}`) * There can only be 1 `locals` block per file * Helmfile hcl `values` are referenced using the `hv` accessor. * Helmfile hcl `locals` are referenced using the `local` accessor. * When the same key is defined multiple times across imported `.hcl` files in `values` blocks, values from later files override those from earlier files (last file loaded wins). Map values are merged per key, while list values are replaced as a whole (i.e. not deep-merged). Mixed-types overrides (e.g. bool -> string) are supported (latest value/type wins). * All cty [standard library functions](`https://pkg.go.dev/github.com/zclconf/go-cty@v1.14.3/cty/function/stdlib`) are available and custom functions could be created in the future Consider the following example : ```terraform # values1.hcl locals { hostname = "host1" } values { domain = "DEV.EXAMPLE.COM" hostnameV1 = "${local.hostname}.${lower(hv.domain)}" # "host1.dev.example.com" } ``` ```terraform # values2.hcl locals { hostname = "host2" } values { hostnameV2 = "${local.hostname}.${hv.domain}" # "host2.DEV.EXAMPLE.COM" } ``` #### Note on Environment.Values vs Values The `{{ .Values.foo }}` syntax is the recommended way of using environment values. Prior to this [pull request](https://github.com/roboll/helmfile/pull/647), environment values were made available through the `{{ .Environment.Values.foo }}` syntax. This is still working but is **deprecated** and the new `{{ .Values.foo }}` syntax should be used instead. You can read more infos about the feature proposal [here](https://github.com/roboll/helmfile/issues/640). ### Environment Secrets Environment Secrets *(not to be confused with Kubernetes Secrets)* are encrypted versions of `Environment Values`. You can list any number of `secrets.yaml` files created using `helm secrets` or `sops`, so that Helmfile could automatically decrypt and merge the secrets into the environment values. First you must have the [helm-secrets](https://github.com/jkroepke/helm-secrets) plugin installed along with a `.sops.yaml` file to configure the method of encryption (this can be in the same directory as your helmfile or in the subdirectory containing your secrets files). Then suppose you have a secret `foo.bar` defined in `environments/production/secrets.yaml`: ```yaml foo.bar: "mysupersecretstring" ``` You can then encrypt it with `helm secrets enc environments/production/secrets.yaml` Then reference that encrypted file in `helmfile.yaml`: ```yaml environments: production: secrets: - environments/production/secrets.yaml --- releases: - name: myapp chart: mychart values: - values.yaml.gotmpl ``` Then the environment secret `foo.bar` can be referenced by the below template expression in your `values.yaml.gotmpl`: ```yaml {{ .Values.foo.bar }} ``` #### Loading remote Environment secrets files Since Helmfile v0.149.0, you can use `go-getter`-style URLs to refer to remote secrets files, the same way as in values files: ```yaml environments: staging: secrets: - git::https://{{ env "GITHUB_PAT" }}@github.com/org/repo.git@/environments/staging.secret.yaml?ref=main - http://$HOSTNAME/artifactory/example-repo-local/test.tgz@environments/staging.secret.yaml production: secrets: - git::https://{{ env "GITHUB_PAT" }}@github.com/org/repo.git@/environments/production.secret.yaml?ref=main - http://$HOSTNAME/artifactory/example-repo-local/test.tgz@environments/production.secret.yaml ``` ### Loading remote Environment values files Since Helmfile v0.118.8, you can use `go-getter`-style URLs to refer to remote values files: ```yaml environments: cluster-azure-us-west: values: - git::https://git.company.org/helmfiles/global/azure.yaml?ref=master - git::https://git.company.org/helmfiles/global/us-west.yaml?ref=master - git::https://gitlab.com/org/repository-name.git@/config/config.test.yaml?ref=main # Public Gilab Repo cluster-gcp-europe-west: values: - git::https://git.company.org/helmfiles/global/gcp.yaml?ref=master - git::https://git.company.org/helmfiles/global/europe-west.yaml?ref=master - git::https://ci:{{ env "CI_JOB_TOKEN" }}@gitlab.com/org/repository-name.git@/config.dev.yaml?ref={{ env "APP_COMMIT_SHA" }} # Private Gitlab Repo staging: values: - git::https://{{ env "GITHUB_PAT" }}@github.com/[$GITHUB_ORGorGITHUB_USER]/repository-name.git@/values.dev.yaml?ref=main #Github Private repo - http://$HOSTNAME/artifactory/example-repo-local/test.tgz@values.yaml #Artifactory url --- releases: - ... ``` Since Helmfile v0.158.0, support more protocols, such as: s3, https, http ``` values: - s3::https://helm-s3-values-example.s3.us-east-2.amazonaws.com/values.yaml - s3://helm-s3-values-example/subdir/values.yaml - https://john:doe@helm-s3-values-example.s3.us-east-2.amazonaws.com/values.yaml - http://helm-s3-values-example.s3.us-east-2.amazonaws.com/values.yaml ``` For more information about the supported protocols see: [go-getter Protocol-Specific Options](https://github.com/hashicorp/go-getter#protocol-specific-options-1). This is particularly useful when you co-locate helmfiles within your project repo but want to reuse the definitions in a global repo. ### Environment values precedence With the introduction of HCL, a new value precedence was introduced over environment values. Here is the order of precedence from least to greatest (the last one overrides all others) 1. `yaml` / `yaml.gotmpl` 2. `hcl` 3. `yaml` secrets Example: --- ```yaml # values1.yaml domain: "dev.example.com" ``` ```terraform # values2.hcl values { domain = "overdev.example.com" env = "dev" willBeOverridden = "override_me" } ``` ```terraform # values3.hcl values { env = "local" } ``` ```yaml # secrets.yml (assuming this one has been encrypted) willBeOverridden: overridden ``` ``` # helmfile.yaml.gotmpl environments: default: values: - values1.yaml - values2.hcl - values3.hcl secrets: - secrets.yml --- releases: - name: random-release [...] values: domain: "{{ .Values.domain }}" # == "overdev.example.com" env: "{{ .Values.env }}" # == "local" willBeOverridden: "{{ .Values.willBeOverridden }}" # == "overridden" ``` ### Environment defaults In addition to `values`, environments support a `defaults` block that provides a separate layer of default values. These are merged **before** `values`, giving `values` higher priority: ```yaml environments: default: defaults: - cluster: dev replicas: 1 values: - replicas: 3 ``` The merge order for environment values is: ``` ┌─────────────────────────────────────────────────────────────────┐ │ 1. Environment defaults (merged first, lowest priority) │ │ 2. Environment values (yaml/yaml.gotmpl) │ │ 3. Environment values (HCL) │ │ 4. Environment secrets (non-HCL, decrypted) │ │ 5. CLI overrides (--state-values-set, --state-values-file) │ └─────────────────────────────────────────────────────────────────┘ ``` In the example above, `{{ .Values.replicas }}` would be `3` (values overrides defaults) and `{{ .Values.cluster }}` would be `dev` (only defined in defaults).