# vals: resolving secret references at the moment of deployment

> vals is a Helm-like values loader that replaces ref+<type> references in YAML and JSON with values pulled from Vault, AWS, GCP, Kubernetes and many other backends. It suits teams that want plain manifests in Git and no secrets in Git.

**helmfile/vals** — Helm-like configuration values loader with support for various sources

- Repository: https://github.com/helmfile/vals
- Stars: 790 · Forks: 104
- Language: Go
- License: Apache-2.0
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/helmfile-vals

## vals resolves references instead of storing values

vals is a tool for managing configuration values and secrets, and the key idea is that a file holds references rather than secrets. A reference looks like a URI with a scheme that names the backend, so a Vault value is written ref+vault://secret/data/foo?proto=http#/mykey inside an otherwise ordinary YAML document. When vals processes that file, the reference is replaced with the real value and the result is printed. The command line help calls it a Helm-like configuration values loader with support for various sources and merge strategies, and the README positions it as the piece that turns a safe, committable manifest into a deployable one. The audience is infrastructure and platform engineers who already run a secrets store and want the secret to appear at one predictable moment: the moment the manifest is applied, not the moment it is committed.

## ref+<type> URIs decide which backend answers a reference

The mechanism is a URI scheme per provider. A document can mix them, and each reference carries its own path and query string, so the same file can pull one value from Vault and another from an HTTP JSON endpoint. The README lists the backends, and it is a long list: Vault, AWS SSM Parameter Store, AWS Secrets Manager, AWS S3, GCP Secrets Manager, GCP KMS, Google Sheets, SOPS-encrypted files, Terraform State, 1Password, 1Password Connect, Doppler, Pulumi State, Kubernetes, Conjur, HCP Vault Secrets, Bitwarden, Yandex Cloud Lockbox, Servercore Secrets Manager, HTTP JSON, Keychain, Scaleway, Delinea SecretServer and Infisical, with CredHub listed as coming soon. Because the scheme is explicit in the string, a value file is self-describing: reading the YAML tells you which store holds each secret, which is a large part of why this pattern survives review.

## eval, exec and env are the three shell entry points

The CLI exposes six commands, and the difference between them is where the resolved values land. eval evaluates a JSON or YAML document, replaces any template expressions in it and prints the result. exec populates environment variables and then executes the command you give it. env renders environment variables only, and the README says the output is consumable by eval or by a tool like direnv. get evaluates a string passed as the first argument, which is the quick path for a single value. ksdecode decodes YAML documents by converting Secret resources' data into stringData, which exists to make Kubernetes Secret manifests compatible with vals eval. version prints the vals version. The split matters operationally: eval and ksdecode touch files, exec touches your process environment, and env is the bridge for tools that want the values without either.

## One binary ships every provider unless you cut it down

By default all providers are compiled into the binary, and the README explains how to make that smaller. Go build tags switch the binary into opt-in mode through custom_providers, and you then name the providers you want:

```sh
# Only include Vault, AWS, GCP and Azure providers
go build -tags "custom_providers,vault,aws,gcp,azure" -o vals ./cmd/vals
```

The tag list covers vault, openbao, aws, gcp, azure, terraform, gitlab, sops, onepassword, doppler, k8s, hcpvaultsecrets, conjur, bitwarden, scaleway, infisical, oci, httpjson, pulumi, secretserver, servercore, keychain and yandex, and all_providers explicitly includes everything. Utility providers, named as echo, file, envsubst and exec, are always included regardless of tags. Building without tags is the simplest path:

```sh
go build -o vals ./cmd/vals
```

The Makefile wraps this with version stamping through ldflags and writes the binary to bin/vals.

## A first reference, from Vault to a rendered document

The README walks through the Vault case in full. A value is written to the store first:

```console
$ vault kv put secret/foo mykey=myvalue
```

Then the template is piped in, with the credentials in the environment:

```console
$ VAULT_TOKEN=yourtoken VAULT_ADDR=http://127.0.0.1:8200/ \
  echo "foo: ref+vault://secret/data/foo?proto=http#/mykey" | vals eval -f -
```

The output is the document with the reference resolved:

```yaml
foo: myvalue
```

Saved to a file, the same thing happens with vals eval -f x.vals.yaml. The README is explicit that this equals what a hand written shell script using the vault CLI and jq would produce, which is the honest way to think about the tool: it is a portable, multi backend version of that pattern, not a new security model.

## Helm output stays secretless until vals eval runs

The Helm integration is the part that makes the GitOps story work. A chart value can hold a reference, so helm template emits a manifest whose Secret still contains a ref+vault string:

```console
$ helm template mysql-1.3.2.tgz --set mysqlPassword='ref+vault://secret/data/foo#/mykey' | vals ksdecode -o yaml -f - | tee manifests.yaml
```

That manifest contains no real secret, so it can be committed. At deploy time the references are resolved into a second file and applied:

```console
$ cat manifests.yaml | ~/p/values/bin/vals eval -f - | tee all.yaml
```

```console
$ kubectl apply -f all.yaml
```

The README draws the security argument from this split: access to the secrets store is needed only by the server or container that pulls the safe manifest and performs the deployment, so access can be omitted from CI. The security here comes from that architecture, not from vals itself.

## Embedding vals in Go needs a cache size

The project is usable as a library, and the API surface is small. You create a runtime with vals.New, passing how many secrets to keep in an LRU cache, and then call Eval on a map of your own:

```go
import "github.com/helmfile/vals"

secretsToCache := 256 // how many secrets to keep in LRU cache
runtime, err := vals.New(secretsToCache)
```

The cache is a real constraint, not a formality. A capacity of 256 means repeated references to the same secret are cheap, but a large manifest with many distinct secrets will evict entries and re-fetch them. The README does not document eviction policy, caching of failed lookups, or whether credentials are refreshed, so a Go integration that depends on long lived behaviour has questions the documentation leaves open. The module path is github.com/helmfile/vals, and the go.mod file requires go 1.26.6.

## Release dates, Apache-2.0, and the gaps in the docs

The project is licensed under Apache-2.0, which permits internal use, modification and redistribution provided the notice and licence text are kept; read the LICENSE file before vendoring it into a product. Maintenance is recent: the last push was on 2026-10-22, and three tagged releases sit in the preceding two months, v0.46.0 on 2026-08-15, v0.46.1 on 2026-09-11 and v0.47.0 on 2026-09-21. The zero major version is the thing to weigh. Consumers should expect interface churn, and the custom build tag mechanism means a provider can be dropped from a build without any other signal. The documentation is also uneven. The README does not document a rollback procedure, does not explain how provider credentials are discovered in every environment, and marks CredHub as coming soon, so a plan that depends on it has no release to point at.

## Conclusion

vals earns its place in pipelines that keep manifests in version control and resolve secrets only at deploy time, and it suits teams already running Vault, AWS or GCP secret stores. It is the wrong tool for a single secret read, where the provider CLI plus jq is less machinery, and a provider you need may still be marked as coming soon, as CredHub is. Before adopting, run vals get on one reference from each backend you plan to use, then confirm the custom_providers build still contains every provider your manifests reference.

## FAQ

### What does vals actually do with a ref+vault reference?

vals eval reads a JSON or YAML document, replaces every reference with the value from the named backend and prints the result. For Vault, a reference looks like ref+vault://secret/data/foo?proto=http#/mykey.

### How do I install vals on macOS or Linux?

Homebrew installs it with brew install vals, and binaries for Linux, macOS and Windows are also published on the releases page. From source, the README shows go build -o vals ./cmd/vals.

### Can I use vals with Helm charts?

Yes. A chart value can hold a reference, and piping helm template output through vals ksdecode produces a manifest with no real secret in it, which can be committed to version control.

### How do I make the vals binary smaller?

Use the custom_providers Go build tag and name only the providers you need, for example go build -tags "custom_providers,vault,aws,gcp,azure" -o vals ./cmd/vals. Without that tag every provider is compiled in.

### Which secret backends does vals support?

The README lists Vault, AWS SSM Parameter Store, AWS Secrets Manager, AWS S3, GCP Secrets Manager, GCP KMS, Google Sheets, SOPS-encrypted files, Terraform State, 1Password, Doppler, Pulumi State, Kubernetes, Conjur, Bitwarden, HTTP JSON, Keychain, Scaleway and Infisical among others, with CredHub marked as coming soon.

### Can vals be used as a Go library?

Yes. You create a runtime with vals.New, passing the number of secrets to keep in an LRU cache, and then call Eval with a map of your own values.

## Sources

- [helmfile/vals on GitHub](https://github.com/helmfile/vals)
- [Issues](https://github.com/helmfile/vals/issues)
- [License: Apache-2.0](https://github.com/helmfile/vals/blob/main/LICENSE)
- [README](https://github.com/helmfile/vals/blob/main/README.md)
- [Releases](https://github.com/helmfile/vals/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/helmfile-vals
