Skip to content

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

Last updated: 2026-03-30