How to Manage Secrets in Git with SOPS and age

Version environment files and deployment secrets more safely by encrypting them in Git with Mozilla SOPS and age instead of leaving passwords and API tokens in plain text.

SecretsGitOpen-source tooling
What you learn

How SOPS and age work together, how to encrypt and edit secret files, how to commit encrypted files safely, and how to recover if you lose the wrong key.

Best for

Self-hosters, small teams, and Git-based deployment flows that need a practical middle ground between plaintext files and a full secret manager platform.

Risk to watch

Encrypted files are only useful if key handling is disciplined. If you lose the decryption key or leak it, your encrypted repo stops being meaningfully safer.

Before you begin

  • A Git repository where you currently keep deployment configs, env files, or app settings.
  • A Linux or macOS shell environment, or WSL on Windows.
  • Permission to install small command-line tools.
  • A plan for where your decryption key backup will live before you encrypt anything important.

Plaintext secrets inside Git cause long-term pain. Even if a repository stays private, secrets spread into forks, backups, CI logs, copied snippets, and old commits. SOPS gives you a better pattern: keep the file structure in Git, but encrypt the sensitive values so the repository stores ciphertext instead of real credentials.

Expected outcome: By the end, you will have an encrypted secret file committed to Git, a working age keypair, and a repeatable way to edit or deploy secrets without keeping plaintext in the repo.

Step 1: Understand the roles of SOPS and age

SOPS is the file encryption tool. It knows how to encrypt YAML, JSON, dotenv, and other text-based files while preserving useful structure.

age is the encryption key system SOPS can use. It is a modern, simple alternative to more awkward GPG-based setups for many people.

Together, they give you a workflow like this:

  1. Create an age keypair.
  2. Tell SOPS which recipient key should be able to decrypt files.
  3. Encrypt secret files before they go into Git.
  4. Decrypt them only when editing or during a controlled deployment step.

This is not the same as a cloud secret manager. It is lighter, cheaper, and easier to adopt in small projects. For many Git-centric self-hosting workflows, that tradeoff is excellent.

Step 2: Install SOPS and age

On Ubuntu or Debian-based systems, you can install age from packages and install SOPS from its release binary if needed. Example:

sudo apt update
sudo apt install -y age curl

SOPS_VERSION="v3.9.0"
curl -LO "https://github.com/getsops/sops/releases/download/${SOPS_VERSION}/sops-${SOPS_VERSION}.linux.arm64"
sudo install -m 0755 "sops-${SOPS_VERSION}.linux.arm64" /usr/local/bin/sops
rm "sops-${SOPS_VERSION}.linux.arm64"

sops --version
age --version

If you are on amd64, replace linux.arm64 with the correct architecture from the SOPS release page. On macOS with Homebrew:

brew install sops age

Always verify you installed the tools successfully before moving on.

Step 3: Create an age key and back it up before you use it

Generate a keypair:

mkdir -p ~/.config/sops/age
age-keygen -o ~/.config/sops/age/keys.txt

Inspect the public recipient line:

grep '^# public key:' ~/.config/sops/age/keys.txt

You will see a line like:

# public key: age1examplepublickeyhere...

That public key can be shared with teammates who need to encrypt files for you. The private key file in keys.txt must stay protected.

Set restrictive permissions:

chmod 600 ~/.config/sops/age/keys.txt

Now make a backup of that key file somewhere safe and offline enough to survive machine failure, for example:

  • An encrypted password manager note or attachment
  • A second secured admin machine
  • An encrypted backup archive you already trust
Warning: If you encrypt important files and then lose the only age private key, SOPS cannot help you recover them. The backup step is not optional.

Step 4: Encrypt your first secret file

Create a small dotenv file to practice with:

mkdir -p secrets
cat > secrets/app.env <<'EOF'
APP_ENV=production
APP_URL=https://example.com
DB_PASSWORD=replace-me
SMTP_PASSWORD=replace-me-too
EOF

Export the recipient key into your shell for convenience:

export SOPS_AGE_RECIPIENTS="age1examplepublickeyhere..."

Encrypt the file in place:

sops --encrypt --in-place secrets/app.env

Open the file and you should now see encrypted values plus SOPS metadata instead of readable secrets.

A cleaner long-term pattern is to add a repository policy file named .sops.yaml so SOPS knows which recipient to use automatically. Example:

creation_rules:
  - path_regex: secrets/.*\.(env|yaml|json)$
    age: age1examplepublickeyhere...

Once that file exists, you can re-encrypt or create future secret files without manually exporting the recipient every time.

Step 5: Edit and use encrypted secrets safely

Edit an encrypted file with SOPS instead of decrypting it by hand:

sops secrets/app.env

SOPS will open a temporary decrypted copy in your editor, then re-encrypt it on save. That is much safer than leaving a plaintext file sitting around in the repository.

To decrypt temporarily for a deployment step:

sops --decrypt secrets/app.env > .env
chmod 600 .env

Use it with Docker Compose:

docker compose --env-file .env up -d

Then remove the temporary plaintext copy when finished:

shred -u .env 2>/dev/null || rm -f .env

If your deployment automation runs in CI, inject the private age key as a protected CI secret and write it to the expected location only inside the job runtime. Avoid printing decrypted secret content in logs.

Step 6: Commit the encrypted file to Git

Check what changed:

git status
git diff -- secrets/app.env .sops.yaml

You should see ciphertext, not raw secret values. Then commit:

git add secrets/app.env .sops.yaml
git commit -m "Add encrypted app secrets with SOPS and age"

Expected outcomes:

  • Your repository stores encrypted secret files instead of plaintext credentials.
  • You can still review structure, file names, and change history in Git.
  • Secrets are decryptable only by people or systems that hold the right age private key.

Rollback and recovery notes

If you encrypted the wrong file or used the wrong recipient, do not panic. As long as you still have the private key that can decrypt it, you can recover.

Useful recovery actions:

sops --decrypt secrets/app.env

sops updatekeys secrets/app.env

git checkout -- secrets/app.env .sops.yaml
  • Use sops --decrypt to confirm the file is still readable with your current key.
  • Use sops updatekeys after updating .sops.yaml if you need to add or rotate recipients.
  • Use git checkout -- ... to discard local mistakes before committing.

If a private key is exposed, treat every secret encrypted to that key as compromised. Rotate the real secrets, generate a new keypair, update recipients, and re-encrypt the files.

Troubleshooting common SOPS and age issues

SOPS says it cannot find a key.
Make sure your private key exists at ~/.config/sops/age/keys.txt or that the relevant environment variable is set in the shell or CI job where you are decrypting.

I encrypted a file, but Git still shows the old plaintext version in history.
Encryption protects future commits, not old ones. Rotate the exposed secret and consider history cleanup if the repository was shared.

The wrong people can still decrypt the file.
Check .sops.yaml and the file metadata. You may have included extra recipients during an earlier test.

My editor leaves swap files around.
That can leak temporary plaintext. Consider using an editor configuration that avoids persistent swap files for secret work, or use a secure temporary filesystem.

CI can decrypt locally, but not on the server.
Verify the deployed environment has the age private key material, correct file permissions, and the same SOPS version or compatible config.

Practical rule: SOPS protects the file at rest in Git. It does not remove the need to protect the machine, CI runner, or shell session where you decrypt it.

What to do next

Once your secrets are versioned more safely, the next step is making the images and packages you run easier to trust. Continue with How to Scan Docker Images and VPS Packages for Vulnerabilities with Trivy.