SOPS
SOPS is a secrets manager for YAML, JSON, ENV, and INI files that encrypts only secret values while keeping file structure readable.
Installation
macOS
# Homebrew
brew install sops
# From source
brew install go
go install github.com/mozilla/sops/v3/cmd/sops@latest
Linux
# Debian/Ubuntu
curl -Lo sops https://github.com/mozilla/sops/releases/download/v3.8.1/sops-v3.8.1.linux.amd64
chmod +x sops
sudo mv sops /usr/local/bin/
# RHEL/CentOS
sudo dnf install sops
# From source
git clone https://github.com/mozilla/sops
cd sops
go build -o sops ./cmd/sops
sudo mv sops /usr/local/bin/
Windows
# Scoop
scoop install sops
# Chocolatey
choco install sops
# Manual download
https://github.com/mozilla/sops/releases/download/v3.8.1/sops-v3.8.1.exe
Basic Operations
Encrypt File
# Interactive encryption (uses configured KMS)
sops -e secrets.yaml > secrets.yaml.enc
# Encrypt in-place
sops -i secrets.yaml
# Encrypt with specific cipher (AWS KMS)
sops --kms arn:aws:kms:us-east-1:123456789012:key/12345678 secrets.yaml
# Encrypt with GCP KMS
sops --gcp-kms \
projects/PROJECT_ID/locations/LOCATION/keyRings/KEYRING/cryptoKeys/KEY \
secrets.yaml
# Encrypt with age
sops --age AGE_PUBLIC_KEY secrets.yaml
# Encrypt with PGP
sops --pgp FINGERPRINT secrets.yaml
Decrypt File
# View decrypted content
sops -d secrets.yaml.enc
# Decrypt in-place
sops -d -i secrets.yaml.enc
# Decrypt and output to file
sops -d secrets.yaml.enc > secrets.yaml
Edit Encrypted File
# Edit file (decrypts -> edit -> re-encrypts)
sops secrets.yaml
# Use specific editor
EDITOR=vim sops secrets.yaml
# Edit without re-encrypting (view only)
sops -d secrets.yaml | less
Encryption Methods
AWS KMS Configuration
# Create encrypted file with KMS
sops --kms arn:aws:kms:us-east-1:123456789012:key/12345678 \
--encrypt secrets.yaml
# Multiple KMS keys (for team access)
sops --kms \
arn:aws:kms:us-east-1:111111111111:key/key1,\
arn:aws:kms:us-east-1:222222222222:key/key2 \
secrets.yaml
# Decrypt (automatic KMS lookup)
sops -d secrets.yaml
GCP KMS Configuration
# Create encrypted file with Cloud KMS
sops --gcp-kms \
projects/my-project/locations/us/keyRings/my-ring/cryptoKeys/my-key \
secrets.yaml
# Multiple keys
sops --gcp-kms \
projects/project1/locations/us/keyRings/ring1/cryptoKeys/key1,\
projects/project2/locations/us/keyRings/ring2/cryptoKeys/key2 \
secrets.yaml
Age Encryption
# Generate age key
age-keygen -o key.txt
# Create encrypted file with age
sops --age $(cat key.txt | grep "public key:" | sed 's/.*//') \
secrets.yaml
# Decrypt (requires private key)
sops -d secrets.yaml
# Multiple age recipients
sops --age "KEY1,KEY2,KEY3" secrets.yaml
PGP Encryption
# Create encrypted file with PGP key
sops --pgp 85D77543B3D8F7CBF2301695B0FFF968627EA2DA secrets.yaml
# Multiple PGP keys
sops --pgp "KEY1,KEY2" secrets.yaml
# Decrypt (requires private key)
sops -d secrets.yaml
Configuration Files
.sops.yaml Configuration
# .sops.yaml - root of repository
creation_rules:
# Rule 1: Production secrets
- path_regex: secrets/prod/.*\.yaml
kms: arn:aws:kms:us-east-1:123456789012:key/12345678
gcp_kms: projects/myproject/locations/us/keyRings/prod/cryptoKeys/key1
age: AGE_PROD_KEY
# Rule 2: Development secrets
- path_regex: secrets/dev/.*\.yaml
age: AGE_DEV_KEY
# Rule 3: Kubernetes secrets
- path_regex: k8s/.*secret\.yaml
kms: arn:aws:kms:us-east-1:123456789012:key/12345678
# Default rule
- kms: arn:aws:kms:us-east-1:123456789012:key/12345678
# What to encrypt in YAML
hc_itamar: true # Ignore comments
key_regex: ^(password|secret|token|api)
# File type
file_type: yaml
.sops.yaml with Multiple Providers
creation_rules:
# AWS KMS primary, age fallback
- path_regex: secrets/.*\.yaml
kms:
- arn:aws:kms:us-east-1:123456789012:key/primary
- arn:aws:kms:us-east-1:123456789012:key/backup
age:
- AGE_KEY1
- AGE_KEY2
gcp_kms:
- projects/myproject/locations/us/keyRings/ring/cryptoKeys/key
File Formats
YAML Files
# secrets.yaml
database:
username: admin
password: ENC[AES256_GCM,data:xxx,...]
host: db.example.com
api:
key: ENC[AES256_GCM,data:yyy,...]
secret: ENC[AES256_GCM,data:zzz,...]
JSON Files
# Encrypt JSON
sops -e secrets.json > secrets.json.enc
# Content remains structured
{
"database": {
"password": "ENC[AES256_GCM,data:...]"
}
}
ENV Files
# Encrypt .env file
sops -e .env > .env.enc
# Content
DATABASE_PASSWORD=ENC[AES256_GCM,data:...]
API_KEY=ENC[AES256_GCM,data:...]
INI Files
# Encrypt INI
sops -e config.ini > config.ini.enc
# Content structure preserved
[database]
password = ENC[AES256_GCM,data:...]
Key Rotation
Rotate AWS KMS Keys
# Create new version with different KMS key
sops -d secrets.yaml | \
sops --kms arn:aws:kms:us-east-1:999999999999:key/newkey -e - > secrets.yaml.new
# Verify new file
sops -d secrets.yaml.new
# Replace old file
mv secrets.yaml.new secrets.yaml
Rotate Age Keys
# Generate new age key
age-keygen -o newkey.txt
# Re-encrypt with new key
sops --age $(cat newkey.txt | grep "public key:" | sed 's/.*//') \
-r -i secrets.yaml
Integration with Tools
Kubernetes Sealed Secrets Integration
# Export SOPS encrypted secret for K8s
sops -d secrets.yaml | \
kubectl create secret generic my-secret --from-file=- --dry-run=client -o yaml | \
kubeseal -o yaml > sealed-secret.yaml
Docker Integration
# Decrypt secrets for Docker build
sops -d secrets.env > .env
docker build --env-file .env .
rm .env # Clean up unencrypted file
Terraform Integration
# Use sops with Terraform
terraform init
sops -d secrets.tfvars.enc > secrets.tfvars
terraform apply -var-file=secrets.tfvars
# Or use tfvars encryption
sops secrets.tfvars
terraform apply -var-file=secrets.tfvars
Git Integration
# Encrypt before committing
sops -i secrets.yaml
git add secrets.yaml
git commit -m "Update encrypted secrets"
# Decrypt when needed
sops secrets.yaml
# .gitignore unencrypted secrets
echo "secrets.yaml.dec" >> .gitignore
echo ".env.local" >> .gitignore
Scripting Examples
Backup Encrypted Secrets
#!/bin/bash
# Backup and encrypt secrets
BACKUP_DIR="/backup/secrets"
SECRETS_FILE="secrets.yaml"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
mkdir -p "$BACKUP_DIR"
# Create backup
sops -d "$SECRETS_FILE" | gzip > "$BACKUP_DIR/backup_$TIMESTAMP.yaml.gz"
# Encrypt backup
age -r AGE_BACKUP_KEY -o "$BACKUP_DIR/backup_$TIMESTAMP.yaml.gz.age" \
"$BACKUP_DIR/backup_$TIMESTAMP.yaml.gz"
rm "$BACKUP_DIR/backup_$TIMESTAMP.yaml.gz"
echo "Backup created: $BACKUP_DIR/backup_$TIMESTAMP.yaml.gz.age"
Rotate All Secrets
#!/bin/bash
# Rotate encryption keys for all secrets
find . -name "*.sops.yaml" -o -name "*secret*.yaml" | while read file; do
echo "Rotating $file..."
# Re-encrypt with new keys from .sops.yaml
sops -r -i "$file"
echo "Rotated $file"
done
Export Decrypted Values
#!/bin/bash
# Export secrets as environment variables
sops -d secrets.yaml | \
yq eval '.[] | .[] | key + "=" + .value' - | \
while read line; do
export "$line"
done
env | grep -E "^(PASSWORD|API|TOKEN)"
Troubleshooting
Common Issues
Issue: “Permission denied” with AWS KMS
# Check AWS credentials
aws sts get-caller-identity
# Verify KMS key access
aws kms describe-key --key-id arn:aws:kms:...
# Check IAM permissions
aws iam get-user
Issue: “age recipient not found”
# Verify age key exists
cat ~/.config/sops/age/keys.txt
# Add missing key
age-keygen -o ~/.config/sops/age/keys.txt
export SOPS_AGE_RECIPIENTS=$(cat ~/.config/sops/age/keys.txt | grep "public key:" | cut -d' ' -f4)
Issue: “PGP key not found”
# List available PGP keys
gpg --list-secret-keys
# Import key if missing
gpg --import private-key.gpg
# Verify key fingerprint
gpg --list-keys | grep FINGERPRINT
Issue: “.sops.yaml not found”
# Create .sops.yaml in repository root
touch .sops.yaml
# Add configuration
cat > .sops.yaml << 'EOF'
creation_rules:
- kms: arn:aws:kms:us-east-1:123456789012:key/12345678
EOF
Security Best Practices
- Store encryption keys separately from encrypted files
- Use different keys for different environments
- Rotate keys regularly (quarterly recommended)
- Restrict access to private keys/KMS keys
- Audit access to encrypted files
- Never commit unencrypted secrets to git
- Use branch protection to prevent secret commits
- Implement code review for secret changes
Related Tools
- age - Simple file encryption
- git-crypt - Git-native encryption
- Sealed Secrets - Kubernetes secrets
- Vault - Centralized secret management
Last updated: 2026-03-30