If your team still passes .env files around in chat (or keeps plaintext secrets in private repos), this is the fix.
This guide shows a practical Linux-first pattern for managing secrets with:
- SOPS for structured file encryption
- age for modern key handling
- Git for auditable change history
- Optional systemd credentials for safer runtime delivery
The goal is simple: keep secrets encrypted at rest in Git, decrypt only where needed, and make the workflow easy enough that people actually follow it.
Why this stack
- SOPS supports YAML/JSON/ENV/INI/binary and works with age, KMS, and PGP.
- age is intentionally small and composable, and its recipient format (
age1...) is straightforward. - Both are open-source and script-friendly, so they fit real CI/CD and self-hosted Linux workflows.
1) Install tools (Linux)
Debian/Ubuntu
sudo apt update
sudo apt install -y age
# SOPS from GitHub release (pick latest for your architecture)
VERSION="v3.10.2"
ARCH="amd64"
curl -Lo sops "https://github.com/getsops/sops/releases/download/${VERSION}/sops-${VERSION}.linux.${ARCH}"
chmod +x sops
sudo mv sops /usr/local/bin/sops
sops --version
age --version
Fedora
sudo dnf install -y age sops
sops --version
age --version
2) Generate an age key pair
Create a dedicated key for this repo or environment.
mkdir -p ~/.config/sops/age
chmod 700 ~/.config/sops/age
age-keygen -o ~/.config/sops/age/keys.txt
chmod 600 ~/.config/sops/age/keys.txt
Print the public recipient:
age-keygen -y ~/.config/sops/age/keys.txt
# age1...
Keep keys.txt private. You commit only encrypted files.
3) Define encryption policy with .sops.yaml
At your repo root:
creation_rules:
- path_regex: secrets/.*\.ya?ml$
age: >-
age1replace_with_your_real_recipient
This tells SOPS to automatically encrypt matching files to your age recipient.
4) Create and encrypt secrets
Example plaintext file:
mkdir -p secrets
cat > secrets/app.enc.yaml <<'YAML'
apiVersion: v1
kind: SecretData
data:
db_user: appuser
db_password: super-secret-value
jwt_signing_key: replace-me
YAML
Encrypt in-place:
sops --encrypt --in-place secrets/app.enc.yaml
Decrypt when needed:
sops --decrypt secrets/app.enc.yaml
Edit safely (decrypt/edit/re-encrypt in one flow):
sops secrets/app.enc.yaml
5) Prevent secret leaks in CI
Add a lightweight policy check so plaintext never lands in main.
#!/usr/bin/env bash
set -euo pipefail
# Fail if any file under secrets/ lacks a SOPS metadata block
while IFS= read -r -d '' f; do
if ! grep -q '^sops:' "$f"; then
echo "[FAIL] Not SOPS-encrypted: $f"
exit 1
fi
done < <(find secrets -type f -name '*.yaml' -print0)
echo "[OK] SOPS metadata present in secrets files"
You can run this in GitHub Actions, GitLab CI, or a pre-commit hook.
6) Runtime delivery on Linux (better than baking secrets into unit files)
For systemd-based services, you can decrypt during deployment and feed secrets as credentials.
Example service snippet:
[Service]
ExecStart=/usr/local/bin/my-app
LoadCredential=app.env:/etc/myapp/credentials/app.env
And your app can read from:
${CREDENTIALS_DIRECTORY}/app.env
This keeps secrets out of your unit file text and aligns with systemdβs credential model.
7) Rotation workflow (minimal downtime)
- Generate new age recipient/key pair.
- Update
.sops.yamlwith new recipient(s). - Re-encrypt files:
sops updatekeys -y secrets/app.enc.yaml
- Deploy and validate.
- Remove old recipient after rollout confirmation.
Tip: during migration, include both old and new recipients temporarily.
Common mistakes to avoid
-
Committing private keys (
~/.config/sops/age/keys.txt) β never. - Storing decrypted files in repo paths where tooling might auto-add them.
- Hardcoding secrets in systemd unit files instead of credentials or secret files with strict permissions.
- Skipping rotation drills until an incident forces one.
Final take
SOPS + age is one of the highest-leverage upgrades you can make to a Linux automation stack:
- encrypted secrets in Git,
- cleaner audits,
- less accidental leakage,
- and a workflow that scales from one VPS to many hosts.
If you want, I can share a follow-up with a full GitHub Actions pipeline that decrypts only on protected runners and validates secret hygiene before deploy.
References
- SOPS docs: https://getsops.io/docs/
- SOPS project/readme: https://github.com/getsops/sops
- age project + usage/man page links: https://github.com/FiloSottile/age
- systemd-creds overview: https://man7.org/linux/man-pages/man1/systemd-creds.1.html
- Forem/Dev.to API docs: https://developers.forem.com/api/v1
Top comments (0)