πŸ“šBook Signing at KubeCon EU 2026Meet us at Booking.com HQ (Mon 18:30-21:00) & vCluster booth #521 (Tue 24 Mar, 12:30-1:30pm) β€” free book giveaway!RSVP Booking.com Event
Helm intermediate ⏱ 15 minutes K8s 1.28+

Helm Secrets Mgmt with SOPS & Age Encryption

Encrypt Helm values files using SOPS with Age or GPG keys. Manage secrets in Git safely with helm-secrets plugin for transparent encrypt/decrypt workflows.

By Luca Berton β€’ β€’ πŸ“– 5 min read

πŸ’‘ Quick Answer: Encrypt Helm values files using SOPS with Age or GPG keys. Manage secrets in Git safely with helm-secrets plugin for transparent encrypt/decrypt workflows.

The Problem

Helm values files often contain database passwords, API keys, and TLS certificates. Storing them in Git as plaintext is a security risk. You need encryption at rest in Git with transparent decryption during helm install/upgrade.

The Solution

Install Tools

# Install SOPS
brew install sops  # macOS
# or download from https://github.com/getsops/sops/releases

# Install Age (modern encryption, replaces GPG)
brew install age
# or: go install filippo.io/age/cmd/...@latest

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

Generate Encryption Keys

# Generate an Age key pair
age-keygen -o ~/.config/sops/age/keys.txt
# Public key: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

# For teams: each member generates their own key
# Share public keys, keep private keys secret

# Create .sops.yaml config in repo root
cat > .sops.yaml << 'EOF'
creation_rules:
  - path_regex: .*secrets.*\.yaml$
    age: >-
      age1abc123...,
      age1def456...
EOF

Encrypt Values Files

# values-secrets.yaml (before encryption)
database:
  password: super-secret-password
  connectionString: postgresql://admin:super-secret-password@db.example.com:5432/mydb
apiKeys:
  stripe: sk_live_xxxxxxxxxxxxxxxx
  sendgrid: SG.xxxxxxxxxxxxxxxxxxxx
tls:
  cert: |
    -----BEGIN CERTIFICATE-----
    MIIEpDCCA4ygAwIBAgIRAJ...
    -----END CERTIFICATE-----
  key: |
    -----BEGIN PRIVATE KEY-----
    MIIEvgIBADANBgkqhki...
    -----END PRIVATE KEY-----
# Encrypt the file
sops --encrypt --in-place values-secrets.yaml

# Or encrypt specific keys only (leave structure visible)
sops --encrypt --encrypted-regex '^(password|connectionString|apiKeys|cert|key)$' \
  --in-place values-secrets.yaml
# values-secrets.yaml (after encryption β€” safe for Git)
database:
  password: ENC[AES256_GCM,data:xxxx,iv:xxxx,tag:xxxx,type:str]
  connectionString: ENC[AES256_GCM,data:xxxx,iv:xxxx,tag:xxxx,type:str]
apiKeys:
  stripe: ENC[AES256_GCM,data:xxxx,iv:xxxx,tag:xxxx,type:str]
  sendgrid: ENC[AES256_GCM,data:xxxx,iv:xxxx,tag:xxxx,type:str]
sops:
  age:
    - recipient: age1abc123...
      enc: |
        -----BEGIN AGE ENCRYPTED FILE-----
        ...
        -----END AGE ENCRYPTED FILE-----

Use with Helm

# helm-secrets transparently decrypts during install/upgrade
helm secrets install my-release ./my-chart \
  --values values.yaml \
  --values values-secrets.yaml

helm secrets upgrade my-release ./my-chart \
  --values values.yaml \
  --values values-secrets.yaml

# Diff before upgrading
helm secrets diff upgrade my-release ./my-chart \
  --values values.yaml \
  --values values-secrets.yaml

# View decrypted values (for debugging)
helm secrets view values-secrets.yaml

# Edit encrypted file (decrypts in editor, re-encrypts on save)
helm secrets edit values-secrets.yaml

ArgoCD Integration

# ArgoCD with helm-secrets
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
spec:
  source:
    repoURL: https://github.com/myorg/my-app
    path: charts/my-app
    helm:
      valueFiles:
        - values.yaml
        - secrets+age-import:///helm-secrets-private-keys/keys.txt?values-secrets.yaml
---
# Mount Age keys in ArgoCD repo-server
apiVersion: v1
kind: Secret
metadata:
  name: helm-secrets-private-keys
  namespace: argocd
stringData:
  keys.txt: |
    # created: 2024-01-01
    # public key: age1abc123...
    AGE-SECRET-KEY-1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Flux Integration

apiVersion: helm.toolkit.fluxcd.io/v2beta2
kind: HelmRelease
metadata:
  name: my-app
spec:
  chart:
    spec:
      chart: ./charts/my-app
      sourceRef:
        kind: GitRepository
        name: my-app
  valuesFrom:
    - kind: Secret
      name: my-app-secrets      # Decrypted by Flux SOPS integration
  decryption:
    provider: sops
    secretRef:
      name: sops-age-key
graph LR
    A[Developer] --> B[sops encrypt]
    B --> C[Encrypted values in Git]
    C --> D{Deploy tool}
    D -->|helm-secrets| E[Decrypt + helm install]
    D -->|ArgoCD| F[Decrypt + sync]
    D -->|Flux| G[Decrypt + reconcile]
    E --> H[Kubernetes Secrets]
    F --> H
    G --> H

Common Issues

IssueCauseFix
Can’t decryptMissing Age private keyExport SOPS_AGE_KEY_FILE
Wrong key used.sops.yaml path_regex mismatchCheck regex matches your file path
ArgoCD can’t decryptMissing key in repo-serverMount Age key as Secret volume
Merge conflicts in encrypted filesSOPS metadata changesDecrypt, merge, re-encrypt

Best Practices

  • Use Age over GPG β€” simpler key management, no keyring complexity
  • Encrypt only secrets β€” use --encrypted-regex to keep structure visible
  • Rotate keys periodically β€” update .sops.yaml and re-encrypt all files
  • Store .sops.yaml in repo β€” everyone uses the same encryption config
  • Never commit plaintext secrets β€” use pre-commit hooks to prevent accidents

Key Takeaways

  • SOPS + Age encrypts secrets at rest in Git β€” safe for version control
  • helm-secrets plugin provides transparent decrypt during install/upgrade
  • ArgoCD and Flux both support SOPS decryption natively
  • Age keys are simpler than GPG β€” one file, no keyring management
  • Encrypt only sensitive values to keep diffs readable
#helm #secrets #sops #encryption #gitops
Luca Berton
Written by Luca Berton

Principal Solutions Architect specializing in Kubernetes, AI/GPU infrastructure, and cloud-native platforms. Author of Kubernetes Recipes and creator of CopyPasteLearn courses.

Kubernetes Recipes book cover

Want More Kubernetes Recipes?

This recipe is from Kubernetes Recipes, our 750-page practical guide with hundreds of production-ready patterns.

Luca Berton Ansible Pilot Ansible by Example Open Empower K8s Recipes Terraform Pilot CopyPasteLearn ProteinLens