SOPS Configuration Reference
Purpose: For platform engineers, provides the .sops.yaml structure, Age key paths, creation rules, and per-directory encryption scoping.
Overview
SOPS uses .sops.yaml files to determine how to encrypt new files. When you run sops --encrypt, it walks up the directory tree from the target file until it finds a .sops.yaml with a matching creation_rules entry. openCenter places .sops.yaml files at multiple levels in the customer repository to scope encryption rules per cluster and per directory.
.sops.yaml Schema
creation_rules:
- path_regex: <regex matching file paths>
encrypted_regex: <regex matching YAML keys to encrypt>
age: <comma-separated Age public keys>
Field Reference
| Field | Type | Required | Description |
|---|---|---|---|
creation_rules | list | yes | Ordered list of rules. First matching rule wins. |
path_regex | string | yes | Go-style regex matched against the file path relative to the .sops.yaml location. |
encrypted_regex | string | no | Regex matched against YAML keys. Only matching keys are encrypted. If omitted, all values are encrypted. |
age | string | yes (for Age) | One or more Age public keys, comma-separated. Files are encrypted to all listed recipients. |
Creation Rules
Rules are evaluated top-to-bottom. The first rule whose path_regex matches the file path is used. Place more specific rules before general ones.
Typical Customer Repository Rules
Root-level .sops.yaml (covers infrastructure secrets):
creation_rules:
- path_regex: infrastructure/clusters/k8s-sandbox/.*
encrypted_regex: "^(data|stringData)$"
age: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Application overlay .sops.yaml (covers service and app secrets):
creation_rules:
- path_regex: services/.*
encrypted_regex: "^(data|stringData)$"
age: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- path_regex: managed-services/.*
encrypted_regex: "^(data|stringData)$"
age: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
path_regex Patterns
| Pattern | Matches |
|---|---|
.* | All files relative to .sops.yaml location |
services/keycloak/.* | Only files under services/keycloak/ |
.*secret.* | Any file with "secret" in the path |
infrastructure/clusters/k8s-sandbox/inventory/credentials/.* | Kubespray credential files for a specific cluster |
Paths are matched relative to the .sops.yaml file's directory, not the repository root.
encrypted_regex Patterns
| Pattern | Effect |
|---|---|
^(data|stringData)$ | Encrypts only Kubernetes Secret values (recommended) |
^(password|token|secret)$ | Encrypts specific named keys |
| (omitted) | Encrypts all values in the file |
The recommended pattern ^(data|stringData)$ keeps metadata (name, namespace, labels, annotations) in plaintext. This allows FluxCD, Kustomize, and git diff to work with the file without decryption.
Age Key Format
Public Key
Used in .sops.yaml as the age recipient. Format:
age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
Always starts with age1, followed by a Bech32-encoded string.
Private Key File
Stored at secrets/age/<cluster>_keys.txt. Format:
# created: 2025-04-15T10:30:00Z
# public key: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
AGE-SECRET-KEY-1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
This file is referenced by:
SOPS_AGE_KEY_FILEenvironment variable (for manualsopscommands)- The
sops-ageKubernetes Secret influx-systemnamespace (for FluxCD decryption)
Dual-Key Format (During Rotation)
During key rotation, the key file contains both old and new keys:
# created: 2025-04-15T10:30:00Z
# public key: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
AGE-SECRET-KEY-1OLDKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# created: 2025-07-14T09:00:00Z
# public key: age1newkeyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
AGE-SECRET-KEY-1NEWKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
And .sops.yaml lists both public keys:
creation_rules:
- path_regex: .*
encrypted_regex: "^(data|stringData)$"
age: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p,age1newkeyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
File Locations in Customer Repository
customers/<org>/
├── .sops.yaml # Root rules (infrastructure)
├── infrastructure/clusters/<cluster>/.sops.yaml # Cluster-specific rules
├── applications/overlays/<cluster>/.sops.yaml # Application overlay rules
└── secrets/age/<cluster>_keys.txt # Age private key (not committed)
The secrets/ directory is listed in .gitignore. Private keys are distributed to the cluster via opencenter cluster setup or manual kubectl create secret.
Behaviors and Edge Cases
- If multiple
.sops.yamlfiles exist in the directory tree, only the nearest one (closest to the file) is used. SOPS does not merge rules across files. - If no
path_regexmatches,sops --encryptfails with "no matching creation rule." - Encrypting an already-encrypted file is a no-op (SOPS detects the
sops:metadata block). - Decrypting requires any one of the listed Age private keys, not all of them.
Further Reading
- SOPS Encryption — step-by-step encryption and decryption
- Key Rotation — dual-key rotation procedure
- Secrets Model — end-to-end secrets architecture