Secrets are encrypted at rest in git using SOPS + age and decrypted just-in-time at deploy time. Each service directory stores an encrypted .env.enc alongside its .env.example.
.env.example → .env (fill values) → .env.enc (encrypt) → git commit
↓
.git (encrypted) → .env.enc → .env (decrypt) → deploy → shred .env
# macOS
brew install sops age
# Linux
# sops: https://github.com/getsops/sops/releases
# age: https://github.com/FiloSottile/age/releasesmkdir -p ~/.config/sops/age
age-keygen -o ~/.config/sops/age/keys.txt
# The public key is printed — add it to .sops.yaml key_groupsEdit .sops.yaml and add your age public key to the key_groups list. Multiple recipients can be listed for team access:
creation_rules:
- path_regex: \.env(\.enc)?$
key_groups:
- age:
- age1existingkey... # existing recipient
- age1yournewkey... # your new keyFor a single service:
# Create .env from .env.example and fill in real values
cp postgres/.env.example postgres/.env
# Edit postgres/.env with real values...
# Encrypt
./stackctl.sh secrets encrypt postgresFor all services at once:
./stackctl.sh secrets encryptThis runs sops --encrypt --input-type dotenv --output-type dotenv .env > .env.enc for each service directory that has a .env file.
Deploy decrypts, renders, deploys, and shreds in one step:
# Deploy a specific service's stack
./stackctl.sh secrets deploy postgres
# Deploy all services
./stackctl.sh secrets deployThe deploy operation:
- Decrypts
.env.enc→.envfor each target service - Regenerates rendered stack files (variable substitution)
- Deploys the relevant Docker Swarm stacks
- Shreds all plaintext
.envfiles
If you need to inspect or edit secrets without deploying:
# Decrypt a single service
./stackctl.sh secrets decrypt postgres
# Decrypt all services
./stackctl.sh secrets decryptRemember to clean up plaintext files after editing:
./stackctl.sh secrets cleanRemove all plaintext .env files that have a corresponding .env.enc:
./stackctl.sh secrets cleanThis uses shred -u when available, falling back to rm -f on systems without shred.
After adding a new recipient to .sops.yaml:
find . -name '.env.enc' -exec sops updatekeys --yes {} \;# Decrypt, edit, re-encrypt
./stackctl.sh secrets decrypt postgres
# Edit postgres/.env...
./stackctl.sh secrets encrypt postgres
./stackctl.sh secrets clean- Commit:
.env.example(placeholders) and.env.enc(encrypted secrets) - Never commit:
.env(plaintext, gitignored) - The
.gitignorerule!*.env.encensures encrypted files are tracked
sopscan't decrypt: Ensure the age private key is at~/.config/sops/age/keys.txtand the corresponding public key is in.sops.yaml.shrednot found: The script falls back torm -f. Installshred(part ofcoreutilson Linux) for secure deletion.sopsoragenot found: Run./stackctl.sh doctorto check prerequisites, or install them manually.