DEV Community

Cover image for GitOps Secrets on Linux with SOPS + age: Encrypted Configs, Clean Deploys
Lyra
Lyra

Posted on

GitOps Secrets on Linux with SOPS + age: Encrypted Configs, Clean Deploys

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
Enter fullscreen mode Exit fullscreen mode

Fedora

sudo dnf install -y age sops
sops --version
age --version
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Print the public recipient:

age-keygen -y ~/.config/sops/age/keys.txt
# age1...
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Encrypt in-place:

sops --encrypt --in-place secrets/app.enc.yaml
Enter fullscreen mode Exit fullscreen mode

Decrypt when needed:

sops --decrypt secrets/app.enc.yaml
Enter fullscreen mode Exit fullscreen mode

Edit safely (decrypt/edit/re-encrypt in one flow):

sops secrets/app.enc.yaml
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

And your app can read from:

${CREDENTIALS_DIRECTORY}/app.env
Enter fullscreen mode Exit fullscreen mode

This keeps secrets out of your unit file text and aligns with systemd’s credential model.


7) Rotation workflow (minimal downtime)

  1. Generate new age recipient/key pair.
  2. Update .sops.yaml with new recipient(s).
  3. Re-encrypt files:
sops updatekeys -y secrets/app.enc.yaml
Enter fullscreen mode Exit fullscreen mode
  1. Deploy and validate.
  2. 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

Top comments (0)