SOPS and Secrets
How secrets (database passwords, API tokens, etc.) are kept out of git in plaintext while still being recoverable if Nexus needs to be rebuilt.
Overview
Plaintext secrets can't go into git. The solution here uses two layers:
- Runtime
.envfiles — live only on Nexus at/home/matt/repos/homelab-docker/<service>/.env, are gitignored via**/.env, and are read directly bydocker compose up -d. During normal operation, SOPS is not involved at all. .env.encfiles — SOPS-encrypted versions committed to git. These are disaster-recovery backups only. If Nexus needs to be rebuilt from scratch, the plaintext.envfiles would be gone;.env.encfiles make them recoverable.
Komodo deploys without touching SOPS. The encrypted files exist purely so secrets can be reconstructed from git if the host is lost.
Tools
| Tool | Purpose |
|---|---|
| SOPS | Encrypts/decrypts files (only values, not keys — diffs stay readable) |
| age | Encryption backend; provides the public/private keypair |
Key Holders
Either machine can encrypt or decrypt.
| Machine | Private key location |
|---|---|
| Nexus | ~/.config/sops/age/keys.txt |
| Atlas | %APPDATA%\sops\age\keys.txt |
Public keys are declared in .sops.yaml at the root of the homelab-docker
repo:
creation_rules:
- path_regex: \.env(\.enc)?$
age: >-
age18p7sc5xrer90x4l5jcn7deu8lputkvatjd223n4ldn2ujr2zhaqq4u78l5,
age1uf500t6wxfxzf5sd723lmynajxva4qy3wt9zvx7rqzk3azmuas7q0m00cp
# First key: Nexus (~/.config/sops/age/keys.txt)
# Second key: Atlas (%APPDATA%\sops\age\keys.txt)
The path_regex covers both .env and .env.enc files automatically.
Files are encrypted for both public keys simultaneously — either private key
can decrypt.
Current Backup Status
| Service | .env.enc in git? |
|---|---|
| bookstack | Yes |
| tandoor | Yes |
| frigate | Yes |
| wyze-bridge | Yes |
| dashy | No (no secrets) |
| homeassistant | No |
| komodo | No |
| pinchflat | No |
| mosquitto | No |
| freshrss | No |
| dispatcharr | No |
Services without .env.enc must be restored from PBS backups or recreated
manually if Nexus is rebuilt.
Common Operations
Decrypt a backup to restore a .env
cd /home/matt/repos/homelab-docker
sops -d --input-type dotenv --output-type dotenv bookstack/.env.enc > bookstack/.env
Edit an encrypted file in-place
SOPS decrypts the file, opens it in $EDITOR, and re-encrypts on save.
sops bookstack/.env.enc
Encrypt a plaintext .env to create or update a backup
cd /home/matt/repos/homelab-docker
sops --encrypt bookstack/.env > bookstack/.env.enc
Then commit the .env.enc file. Never commit the plaintext .env.
Adding a New Service with Secrets
-
Create the plaintext
.envin the service directory on Nexus:/home/matt/repos/homelab-docker/<service>/.envIt is gitignored automatically by**/.env— no extra config needed. -
Add
env_file: - ./.envto the service'sdocker-compose.yml. -
Encrypt a backup:
cd /home/matt/repos/homelab-docker sops --encrypt <service>/.env > <service>/.env.enc -
Commit the
.env.encfile:git add <service>/.env.enc git commit -m "feat(<service>): add SOPS-encrypted env backup"
Installing SOPS and age
Nexus (Ubuntu Server)
# Install age
sudo apt update
sudo apt install age
# Install sops (check https://github.com/getsops/sops/releases for latest)
SOPS_VERSION="3.9.1"
wget https://github.com/getsops/sops/releases/download/v${SOPS_VERSION}/sops-v${SOPS_VERSION}.linux.amd64 \
-O /tmp/sops
sudo mv /tmp/sops /usr/local/bin/sops
sudo chmod +x /usr/local/bin/sops
# Point SOPS at the key file automatically
echo 'export SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt' >> ~/.bashrc
source ~/.bashrc
# Verify
sops --version
age --version
Atlas (Windows 11)
Install via winget or download binaries from the GitHub releases pages:
Add both executables to your PATH. The key file lives at
%APPDATA%\sops\age\keys.txt.
Generating a new key pair (bootstrap only)
Run once on each machine. Store the output (keys.txt) in Bitwarden — if the
file is lost, all encrypted backups become unrecoverable.
mkdir -p ~/.config/sops/age
age-keygen -o ~/.config/sops/age/keys.txt
# Output: Public key: age1... ← add this to .sops.yaml
After generating, add the new public key to .sops.yaml and re-encrypt all
existing .env.enc files so the new key can decrypt them.
Troubleshooting
"no age keys found" / SOPS can't find a key
Ensure the key file exists and the env var points to it:
ls -la ~/.config/sops/age/keys.txt
echo $SOPS_AGE_KEY_FILE
If the env var is unset, either source .bashrc or pass the path explicitly:
SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt sops -d bookstack/.env.enc
Decrypt fails with "no private key found for recipient"
You are on a machine that is not a key holder. Only Nexus and Atlas hold private keys. Transfer the file to one of those machines to decrypt it.
Verify the public key in .sops.yaml matches the private key on this machine
age-keygen -y ~/.config/sops/age/keys.txt
# Output should match one of the public keys in .sops.yaml
Never commit a plaintext .env
The .gitignore in homelab-docker blocks **/.env and komodo/compose.env.
Run git status before committing and verify no plaintext env files appear as
staged.
Automation
Infrastructure
Services
- Dashy
- Bookstack
- Tandoor
- Home Assistant
- Pinchflat
- FreshRSS
- Frigate
- Wyze-Bridge
- Mosquitto
- Dispatcharr
- Proxmox LXC Services
Operations
- How to Update Apps
- Backup & Restore Strategy
- Maintenance Schedule
- Useful Commands
- Restore Proxmox Containers
- NAS NFS Share for LXC
- SSH Public Key onto Nexus