Managing Secrets with helm-secrets and SOPS

Keep encrypted values files in git and let the helm-secrets plugin decrypt them at deploy time with SOPS, so passwords never sit in plaintext.

Plaintext passwords in a values file is the mistake everyone makes once. The values you pass to Helm end up base64-encoded in a Secret — which is not encryption — and worse, that file tends to get committed. You need real encryption at rest that still works with a normal Helm workflow.

SOPS + helm-secrets

SOPS (Secrets OPerationS, from Mozilla) encrypts only the values in a YAML file, leaving the keys readable, so a diff still shows which secret changed without revealing what it changed to. It encrypts against a KMS key — AWS KMS, GCP KMS, Azure Key Vault, or age/PGP for local setups.

The helm-secrets plugin wires SOPS into Helm. You reference an encrypted file with the secrets:// prefix (or helm secrets upgrade), and the plugin transparently decrypts it to a temp file, feeds it to Helm, then shreds it. Your git history holds only ciphertext; decryption needs the KMS key that CI and operators hold.

Why this beats the alternatives for a lot of teams

  • Secrets live with the chart in git — reviewable, versioned, no separate secret store to keep in sync.
  • Access is controlled by who can use the KMS key, not by filesystem permissions.
  • It composes with normal -f layering: plain values.yaml plus an encrypted secrets.yaml.
For clusters that already run the External Secrets Operator or Vault, prefer having the chart reference an existing Secret. helm-secrets shines when you want GitOps-friendly secrets without standing up extra infrastructure.

Example

Example · bash
# One-time: install the plugin and encrypt a values file with SOPS + age
helm plugin install https://github.com/jkroepke/helm-secrets
export SOPS_AGE_RECIPIENTS=age1ql3z7...   # your team's public key
sops --encrypt --encrypted-regex '^(password|token|apiKey)$' \
  secrets.yaml > secrets.enc.yaml         # commit ONLY the .enc file

# Deploy: plaintext base values + transparently-decrypted secrets
helm secrets upgrade --install my-web ./mychart \
  -f values-prod.yaml \
  -f secrets://secrets.enc.yaml \
  -n web --atomic

# Inspect what is inside without leaving plaintext on disk
helm secrets decrypt secrets.enc.yaml

When to use it

  • A developer checks encrypted values files into Git using helm-secrets with SOPS so secrets are version-controlled and auditable without ever storing plaintext credentials in the repository.
  • A platform team configures SOPS to use an AWS KMS key so only CI runners with the correct IAM role can decrypt the values files, eliminating shared passwords.
  • An operator runs helm secrets upgrade to transparently decrypt a SOPS-encrypted values file and pass it to helm upgrade without ever writing the plaintext to disk.

More examples

Encrypt a secrets file with SOPS

Installs the helm-secrets plugin and encrypts a values file with an age public key, producing a ciphertext file safe to commit.

Example · bash
# Install helm-secrets plugin
helm plugin install https://github.com/jkroepke/helm-secrets

# Encrypt with age key
sops --encrypt --age $(cat ~/.config/sops/age/keys.txt | grep 'public key' | cut -d: -f2) \
  values-secrets.yaml > values-secrets.enc.yaml

Deploy with encrypted values

Uses the helm-secrets wrapper to decrypt the encrypted file on the fly and pass plaintext values to helm, without writing secrets to disk.

Example · bash
helm secrets upgrade --install my-app ./myapp \
  -f values-prod.yaml \
  -f values-secrets.enc.yaml \
  -n production

SOPS config for KMS

Configures SOPS to encrypt any secrets values file using an AWS KMS key, restricting decryption to IAM roles with key access.

Example · yaml
# .sops.yaml — root of the repo
creation_rules:
  - path_regex: values-secrets.*\.yaml$
    kms: arn:aws:kms:us-east-1:123456789:key/mrk-abc123
    aws_profile: ci-deployer

Discussion

  • Be the first to comment on this lesson.