10. Configure SOPS and AGE for secret encryption

10.1. Basic SOPS / AGE setup

  1. Install SOPS([sops]) and AGE([age]) utilities

  2. Create an AGE key pair. The generated file (age.agekey in the example below) contains the secret key (store its content safely)

 age-keygen -o age.agekey
Public key: age1ttax...REDACTED

→ cat age.agekey
# created: 2025-06-25T21:10:45-07:00
# public key: age1ttax...REDACTED
AGE-SECRET-KEY-1N...REDACTED
  1. Use the sops command to encrypt a secret:

 cat mysecrets.yaml
---
apiVersion: v1
kind: Secret
metadata:
  name: mysecrets
type: Opaque
stringData:
  username: "alice"
  password: "supersecret" sops --age=age1t...REDACTED \
 --encrypt \
 --encrypted-regex '^(data|stringData)$' \
 --in-place mysecrets.yaml

→ cat mysecrets.yaml
apiVersion: v1
kind: Secret
metadata:
    name: mysecrets
type: Opaque
stringData:
    username: ENC[AES256_GCM,data:UicDDU0=,iv:JwuhuMYAsALZnAcqslMD9XwlykUyFv2bljIqQYiGKMs=,tag:As2FLh4Ao+lZSTEjxlxq4A==,type:str]
    password: ENC[AES256_GCM,data:751c4RypvRiADwc=,iv:sta8Qknev6t06oA6a65HvAbuS1GA47peXYV/Ynvci/w=,tag:mK9FGEbi143hlWQHBSAskA==,type:str]
sops:
    age:
        - recipient: age1t...REDACTED
          enc: |
            -----BEGIN AGE ENCRYPTED FILE-----
            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkYjU4ZFdQS3lzNzBKSmJK
...
...
  1. Adding a .sops.yaml config file to the repo, we can avoid typing some of the parameters each time we invoke sops:

 cat .sops.yaml
creation_rules:
  - path_regex: .*.yaml
    encrypted_regex: ^(data|stringData)$
    age: age1t...REDACTED

# the sops command above can be now reduced to: sops --encrypt --in-place mysecrets.yaml
  1. To decrypt in the terminal, add the AGE key to $HOME/.config/sops/age/keys.txt and use sops decrypt (Ref. ([sops] section “Encrypting using age”) for more details)

 cat age.agekey >> $HOME/.config/sops/age/keys.txt

→ sops decrypt mysecrets.yaml

10.2. Using SOPS/AGE with Flux

Flux guide: [flux-sops-1]

1. Add the AGE secret key to the k8s cluster as a secret, in the flux-system namespace

 cat age.agekey |
kubectl create secret generic sops-age \
--namespace=flux-system \
--from-file=age.agekey=/dev/stdin
secret/sops-age created

→ kubectl -n flux-system get secrets sops-age -o yaml
apiVersion: v1
data:
  age.agekey: REDACTED
kind: Secret
...

#(to view the decrypted value) kubectl -n flux-system \
get secrets sops-age -o yaml \
| yq ".data.\"age.agekey\"" \
| base64 -d

2. The sops-age secret need to be specified in the Flux Kustomization for the component that uses the key for encryption, by adding a decryption: key in the spec, for example:

 cat clusters/my-cluster/infra.yaml
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: infra-monitoring
  namespace: flux-system
spec:
  interval: 1h
  retryInterval: 1m
  timeout: 5m
  sourceRef:
    kind: GitRepository
    name: flux-system
  path: ./infra/monitoring
  prune: true
  wait: true
  # Decryption configuration
  decryption:
    provider: sops
    secretRef:
      name: sops-age

3. Add a .sops.yaml file to the repository root, containing the public key (ref. above section) (to make sure we use the same age key when encrypting secrets)

10.3. Using SOPS/AGE with Flux Helm Releases

Flux guide: [flux-sops-2]

When using sops to encrypt values to be used in Flux Helm releases, the guide recommend to use a Kustomize secret generator (to trigger a Helm release upgrade every time the encrypted secret values change)

10.4. References