Storing unencrypted API keys, database credentials, and certificates in version control repositories is one of the most common causes of enterprise security breaches. While tools like git-crypt or raw GPG scripts provide basic encryption, they treat files as opaque binary blobs, making code reviews and Git diff inspections impossible. SOPS (Secrets OPerationS, developed by Mozilla) solves this by encrypting only the values inside structured files (such as YAML, JSON, ENV, and INI) while leaving the keys and structure in readable plaintext.
This tutorial explains how to install SOPS on Linux, integrate modern age encryption keys, encrypt and edit configuration files seamlessly, and decrypt secrets within automated CI/CD and GitOps workflows.
Installing SOPS on Linux Distributions
SOPS is distributed as a single statically linked binary with zero external dependencies and is available across major Linux packaging ecosystems.
Method 1: Downloading Official GitHub Release Binaries (Recommended)
Fetch the latest stable x86_64 binary directly from the GitHub releases page:
# Download the latest Linux release
curl -L https://github.com/getsops/sops/releases/latest/download/sops-v3.9.4.linux.amd64 -o /tmp/sops
# Make executable and move to system path
chmod +x /tmp/sops
sudo mv /tmp/sops /usr/local/bin/sops
# Verify installation
sops --version
Method 2: Installing via Native Package Managers
On Arch Linux and Manjaro:
sudo pacman -S sops
On systems running Homebrew on Linux:
brew install sops
Setting Up age Encryption Keys with SOPS
While SOPS supports PGP, AWS KMS, Google Cloud KMS, and Azure Key Vault, the modern lightweight cryptographic standard for local and Kubernetes secret management is age.
- Install
ageon your Linux machine:sudo apt install age -y # Debian/Ubuntu sudo dnf install age -y # Fedora/RHEL - Generate a dedicated secret keypair:
mkdir -p ~/.config/sops/age age-keygen -o ~/.config/sops/age/keys.txt - Export the key location in your shell environment (add this to
~/.bashrcor~/.zshrc):export SOPS_AGE_KEY_FILE="$HOME/.config/sops/age/keys.txt" - Inspect your public recipient key:
(The output begins withage-keygen -y ~/.config/sops/age/keys.txtage1...).
Configuring Declarative Encryption Rules (.sops.yaml)
To avoid typing lengthy key flags for every command, define a root configuration file named .sops.yaml in your project repository:
creation_rules:
- path_regex: '.*\.encrypted\.ya?ml$'
age: 'age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p'
- path_regex: '.*\.env$'
age: 'age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p'
This declarative rule instructs SOPS to automatically encrypt any file ending in .encrypted.yaml or .env using your designated public age recipient.
| SOPS Command | Syntax Example | Operational Function |
|---|---|---|
| sops -e (Encrypt) | sops -e secrets.yaml > secrets.enc.yaml |
Encrypts values in-place or redirects encrypted output to a new file. |
| sops -d (Decrypt) | sops -d secrets.enc.yaml |
Decrypts file content and prints plaintext to stdout or stdout pipe. |
| sops <file> (In-Place Edit) | sops secrets.enc.yaml |
Decrypts file in RAM, launches your $EDITOR, and re-encrypts upon save. |
| sops exec-env | sops exec-env secrets.env 'npm start' |
Injects decrypted environment variables directly into a running process. |
Editing Encrypted Files In-Place
One of SOPS’s most powerful features is transparent in-place editing:
- Create an initial secret YAML file:
sops secrets.encrypted.yaml - Your default text editor (defined by
$EDITOR, e.g.nanoorvim) launches. - Type your configuration keys and values:
database: host: postgres.internal username: app_user password: SuperSecretDatabasePassword123! api_tokens: stripe: sk_live_51Nz849129038401 - Save and exit your editor.
- Inspect the file on disk:
cat secrets.encrypted.yaml. The keys (database,host,password) remain completely visible, but the values are replaced with encrypted ciphertext strings (e.g.ENC[AES256_GCM,data:...]) alongside cryptographic integrity hashes.
GitOps & Code Review Advantage
Because keys remain unencrypted in Git diffs, peer reviewers can easily verify that a new configuration key was added or an endpoint URL was altered without requiring access to the production decryption keys.
Deploying Secrets Without Writing Decrypted Files to Disk
To prevent leaking unencrypted secrets to temporary disk logs, inject decrypted variables straight into memory:
sops exec-env .env.encrypted 'python app.py'
In Kubernetes deployments, you can pipe decrypted manifests directly to kubectl:
sops -d k8s-secrets.encrypted.yaml | kubectl apply -f -
Troubleshooting Common SOPS Errors
If SOPS fails to encrypt or decrypt configuration payloads, verify these settings:
- Error: Could not find key file: If SOPS returns
no age keys found, ensure the environment variableSOPS_AGE_KEY_FILEpoints to yourkeys.txtfile path, or specify the key directly viaexport SOPS_AGE_KEY=$(cat keys.txt)in CI/CD pipelines. - Integrity Check Failed (MAC Mismatch): SOPS embeds a Message Authentication Code (MAC) verifying that keys and unencrypted metadata were not tampered with. If someone manually modifies the encrypted YAML file outside of SOPS, decryption aborts to prevent malicious payload injections.
- Multiple Recipients / Team Collaboration: To allow multiple developers or CI/CD runners to decrypt the same file, add their public age keys as a comma-separated list under the
age:directive in.sops.yamland re-encrypt the file usingsops updatekeys secrets.encrypted.yaml.