SOPS + age: keep encrypted configs in Git
Added 2026-10-11. Requires SOPS and age; run the example in a separate practice directory first.
Problem
Secret settings are excluded from Git, but they are passed around by hand and get lost during migrations. A plain .env must not live in the repository history.
Solution
SOPS is "an editor of encrypted files that supports YAML, JSON, ENV, INI and BINARY formats and encrypts with AWS KMS, GCP KMS, Azure Key Vault, HuaweiCloud KMS, age, and PGP". It encrypts configuration values, and the public age recipient defines who can decrypt them. Keep the private key outside the repository and back it up separately from the encrypted files. For production CI, decide in advance which runner gets the key and under what conditions.
Example
Create a dedicated key outside Git. test ! -e prevents accidentally overwriting an existing key. Run the block as a Bash script.
#!/usr/bin/env bash
set -euo pipefail
umask 077
key_dir="$HOME/.config/devops-kb-secrets"
mkdir -p "$key_dir"
export SOPS_AGE_KEY_FILE="$key_dir/age-key.txt"
test ! -e "$SOPS_AGE_KEY_FILE"
age-keygen -o "$SOPS_AGE_KEY_FILE"
age-keygen -y "$SOPS_AGE_KEY_FILE"
The last command prints the public recipient. In the practice directory, create .sops.yaml, replacing REPLACE_WITH_PUBLIC_AGE_RECIPIENT with that value:
creation_rules:
- path_regex: '^secrets/.*\.yaml$'
age: 'REPLACE_WITH_PUBLIC_AGE_RECIPIENT'
All values in the YAML get encrypted; key names and structure stay visible. Do the first check on obviously non-secret data:
#!/usr/bin/env bash
set -euo pipefail
umask 077
export SOPS_AGE_KEY_FILE="$HOME/.config/devops-kb-secrets/age-key.txt"
mkdir -p secrets
test ! -e secrets/demo.yaml
printf 'token: DEMO_NOT_A_REAL_SECRET\n' > secrets/demo.yaml
sops encrypt --in-place secrets/demo.yaml
sops decrypt secrets/demo.yaml > /dev/null
Before adding to Git, check that token contains ENC[...] and the file has sops metadata. For further work use sops edit secrets/demo.yaml; never send decrypted output to a CI log. The value in the example is for testing only. .sops.yaml itself can live in Git: it holds the public recipient, not the private key.
When to use
For a small set of static configs and GitOps. If you need short-lived credentials and centralized access revocation, SOPS alone isn't enough. Encrypting a new version doesn't remove the old plaintext secret from history: a previously exposed secret has to be replaced.
Source
How to apply it yourself
- Verify the round trip on a practice file and restoring the key from backup.
- Separate keys per environment and document the recipients.
- Make sure neither the private key nor a plaintext secret shows up in diffs, Git or logs.
- After safely delivering one real config, mark it "Studied".