Two sets of templates to copy and use:
- Configuration template (
.sops.yaml): sops client encryption rules, specifying “which files are encrypted with which public keys”- Encryption template (
host/sops.nix): the sops-nix NixOS module, specifying “what to decrypt at runtime, where to write it, and who can read it”This article explains general-purpose templates checked against current upstream SOPS and sops-nix source code. The SOPS CLI version is not the sops-nix module version. Before deploying a project, still check the sops-nix revision pinned in flake.lock.
Read before use
AGE_RECIPIENT_PLACEHOLDER_NOT_VALIDin the templates is a placeholder. Before copying and using a template, replace it with your own real public key; otherwise, sops parsing will fail withmalformed recipient. The same applies to sections marked with# 示例comments.
Template one: .sops.yaml (sops client configuration)#
Place it at the repository root. By default, the current SOPS CLI searches upward from the working directory for
.sops.yaml; when invoking it across directories, specify--configexplicitly. The target file path is matched against rules relative to the configuration directory. Rules are matched from top to bottom, and the first match wins, so put more specific rules first.
# ============================================================
# .sops.yaml — sops 客户端加密规则模板
# ============================================================
# keys: 变量区(可选)——给公钥起别名,后面用 *别名 引用
# 好处:定义一次、多处引用;改 key 只改这里
keys:
- &server-key AGE_RECIPIENT_PLACEHOLDER_NOT_VALID # SSH host key 的 age 公钥
- &backup AGE_RECIPIENT_PLACEHOLDER_NOT_VALID # 备用公钥(示例)
creation_rules:
# ================= 单规则 =================
# 一条规则 = 「匹配某类文件 → 用这些 key 加密」
# 规则从上到下,第一条匹配生效
- path_regex: host/secrets/secrets\.yaml$
# path_regex: Go 风格正则,匹配相对 .sops.yaml 所在目录的路径
# \. = 转义点号(正则的 . 匹配任意字符,必须转义)
# $ = 锚定结尾,防止误匹配 secrets.yaml.bak
key_groups:
# key_groups: 密钥组列表(高级用)。每个组内可以不同 key,shamir_threshold 控制阈值
- age:
- *server-key
# age: 用 age 公钥加密(数组写法,可以每把 key 一行 + 注释)
# pgp: 也能混用 PGP 指纹
# kms: 也能混用 AWS KMS ARN
# ================= 多接收者(平铺写法) =================
- path_regex: host/secrets/team\.yaml$
age: "AGE_RECIPIENT_PLACEHOLDER_NOT_VALID,AGE_RECIPIENT_PLACEHOLDER_NOT_VALID"
# age: 逗号分隔的字符串写法。任何一把对应私钥都能解。
# 逗号分隔字符串可以在字符串外加 YAML 注释;需要逐个接收者注释时用 key_groups 数组
# ================= 不同文件用不同 key =================
- path_regex: clusters/dev/.*secrets.*\.yaml$
encrypted_regex: ^(data|stringData)$
# encrypted_regex: 只加密匹配该正则的 key(K8s Secret 场景,metadata 保持明文)
age: "AGE_RECIPIENT_PLACEHOLDER_NOT_VALID"
# ================= 阈值解密(Shamir 门限) =================
- shamir_threshold: 2
# shamir_threshold: 需要几个 key_groups 满足才能解密(Shamir 秘密分享)
path_regex: \.risc\.yaml$
key_groups:
- age:
- "AGE_RECIPIENT_PLACEHOLDER_NOT_VALID"
- gcp_kms:
- resource_id: projects/EXAMPLE_PROJECT/locations/global/keyRings/EXAMPLE_RING/cryptoKeys/EXAMPLE_KEY
# ================= 兜底规则(可选,放最后) =================
- path_regex: .*
# 没有 path_regex 或 .* = 匹配所有文件的兜底规则
age: "AGE_RECIPIENT_PLACEHOLDER_NOT_VALID"creation_rules common fields: quick reference#
| Field | Purpose | Type |
|---|---|---|
path_regex |
Match file paths (relative to the .sops.yaml directory) | Regular expression string |
age |
age recipients; comma-separated string at the top level, array within key_groups | Depends on the level |
pgp |
PGP fingerprints; comma-separated string at the top level, array within key_groups | Depends on the level |
kms |
AWS KMS ARNs; string at the top level, array of objects in key_groups | Depends on the level |
aws_profile |
AWS profile name | str |
gcp_kms |
GCP KMS resource IDs; array of resource_id objects in key_groups | Depends on the level |
azure_keyvault |
Azure Key Vault URL | str |
hc_vault_transit_uri |
HashiCorp Vault transit URI | str |
key_groups |
Key groups (one key per line allows comments) | list |
shamir_threshold |
Number of groups required for decryption | int |
encrypted_regex |
Encrypt only matching keys | Regular expression |
unencrypted_regex |
Keys to leave in plaintext | Regular expression |
encrypted_suffix |
Encrypt keys ending with this suffix | str |
unencrypted_suffix |
Leave keys ending with this suffix in plaintext (default: _unencrypted) |
str |
encrypted_comment_regex |
Encrypt matching comments | Regular expression |
unencrypted_comment_regex |
Comments to leave in plaintext | Regular expression |
mac_only_encrypted |
MAC covers encrypted values only | bool |
When to use each form
- One key / one file: a flat string such as
age: "..."is simplest- Multiple keys with ownership comments: use
key_groups+ arrays, with a comment on each key's line- Multiple environments / teams: one rule per environment, specific rules first and fallback last
Template two: host/sops.nix (sops-nix NixOS module)#
System-level configuration specifying the runtime “decryption source, output location, file ownership, and permissions.”
{ config, pkgs, lib, ... }:
{
sops = {
# ============ 顶层 ============
defaultSopsFile = ./secrets/secrets.yaml;
# type: path | 默认密文文件(所有 secret 的默认来源)
defaultSopsFormat = "yaml";
# type: str | 默认加密文件格式
# enum: yaml | json | dotenv | ini | binary
defaultSopsKey = null;
# null = 默认按 secret 名选择;空字符串 "" 才表示整个文件
useSystemdActivation = true;
# type: bool | true=用 systemd service 解密;false=NixOS activation script
# 按实际用户管理、解密时序与挂载依赖选择
# validationPackage 通常保留模块默认的 sops-install-secrets 验证器
# type: package | 此处不是任意 SOPS CLI 包
# ============ age(解密密钥来源) ============
age = {
keyFile = null;
# type: nullOr path | 独立 age 私钥路径(null = 不用独立 key)
# 可与 sshKeyPaths 共存;仅在希望单一来源时主动关闭其他来源
generateKey = false;
# type: bool | true=若 keyFile 不存在则自动生成
sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
# type: listOf path | 从 SSH key 导入作为 age 解密密钥
# 复用机器身份;仍需保管、备份并保证部署时可读
# 默认值: openssh hostKeys 里的 ed25519 key
};
# ============ secrets(每个机密单独配置) ============
secrets = {
app_env = {
sopsFile = ./secrets/secrets.yaml;
# type: path | 覆盖默认文件;不填写则继承 defaultSopsFile
format = "yaml";
# type: enum | 覆盖格式(yaml/json/dotenv/ini/binary)
# 默认: 继承 defaultSopsFormat;不是按扩展名推断
path = "/run/secrets/app_env";
# type: str | 解密后写到哪
# 默认: /run/secrets/<name>(普通)/ /run/secrets-for-users/<name>(neededForUsers)
owner = null;
# type: nullOr str | 文件属主(null = root)
group = null;
# type: nullOr str | 文件属组(默认 = owner 的 group)
mode = "0400";
# type: str | 文件权限(默认 0400,仅属主可读)
neededForUsers = false;
# type: bool | true=在 setupSecretsForUsers 阶段解密(早于用户就绪)
# 该阶段在用户创建前;owner/group 必须为 root,不能指定尚未存在的应用用户
restartUnits = [ ];
# type: listOf str | 解密后要重启的 systemd 服务
reloadUnits = [ ];
# type: listOf str | 解密后要 reload 的 systemd 服务
};
};
# ============ gnupg(可选,可与 age 来源共存) ============
gnupg = {
home = null; # GnuPG home 目录
sshKeyPaths = [ ]; # 用 SSH key 作 GPG 密钥
};
};
# ============ 消费方示例 ============
# 服务用 EnvironmentFile 读解密后的环境变量
systemd.services.example = {
after = [ "sops-install-secrets.service" ];
requires = [ "sops-install-secrets.service" ];
serviceConfig.EnvironmentFile = "/run/secrets/app_env";
};
}sops.nix common options: quick reference#
| Option | Type | Default | Description |
|---|---|---|---|
sops.defaultSopsFile |
path | — | Default encrypted file |
sops.defaultSopsFormat |
str | yaml |
Default format |
sops.defaultSopsKey |
nullOr str | null | Default key name to retrieve |
sops.useSystemdActivation |
bool | Depends on sysusers/userborn | true = decrypt through a systemd service; the default also depends on options such as userborn |
sops.validationPackage |
package | sops-install-secrets | Validation tool |
sops.age.keyFile |
nullOr path | null | Separate age private key |
sops.age.generateKey |
bool | false | Generate if keyFile does not exist |
sops.age.sshKeyPaths |
listOf path | hostKeys | Decrypt with an SSH key |
sops.secrets.<n>.path |
str | /run/secrets/<n> |
Output path |
sops.secrets.<n>.owner |
nullOr str | null | Owner |
sops.secrets.<n>.group |
nullOr str | The owner's group | Group |
sops.secrets.<n>.mode |
str | 0400 |
Permissions |
sops.secrets.<n>.sopsFile |
path | defaultSopsFile | Override the encrypted file |
sops.secrets.<n>.format |
enum | defaultSopsFormat | Override the format |
sops.secrets.<n>.neededForUsers |
bool | false | Decrypt during the user stage |
sops.secrets.<n>.restartUnits |
listOf str | [ ] | Restart after decryption |
sops.secrets.<n>.reloadUnits |
listOf str | [ ] | Reload after decryption |
Decryption sources can coexist
age.keyFileandage.sshKeyPathsare not mutually exclusive, and age and GnuPG sources can also be declared together. Choosing sources is a permissions and maintenance decision; do not misrepresent mechanisms that can coexist as a type constraint. Key locations must be readable at the selected decryption stage, and private keys must never enter the Nix store.
Template three: sops-nix runtime configuration rendering#
The first two templates are examples of CLI and module configuration, a different concept from actual sops.templates. The latter contains only placeholders during Nix evaluation; after secrets are decrypted, the installer substitutes the actual contents, keeping plaintext out of the store.
{ config, ... }:
{
sops.secrets.api_token = { };
sops.templates."example-app.env" = {
content = ''
API_TOKEN=${config.sops.placeholder.api_token}
'';
mode = "0400";
};
systemd.services.example-app = {
after = [ "sops-install-secrets.service" ];
requires = [ "sops-install-secrets.service" ];
serviceConfig.EnvironmentFile = config.sops.templates."example-app.env".path;
};
}Templates normally output to /run/secrets/rendered/<name>; referring to them through .path is more robust. You can set content/file, path, owner/group, mode, and restartUnits/reloadUnits to run on changes. Placeholder replacement does not automatically escape JSON, YAML, shell syntax, or EnvironmentFile contents. If a secret contains newlines, quotes, or special characters, design around the consuming format. The example above requires values safely encoded for EnvironmentFile; for complex secrets, it is preferable for the application to read credential files directly. This is only a snippet, not a complete deployed service.
Related#
- Understanding the configuration file → sops usage guide
- Key replacement workflow → sops migration guide
- Reference: sops configuration documentation | sops-nix README
References & further reading
- Official SOPS documentation ↗
- SOPS CLI source code ↗
- SOPS configuration source code ↗
- Official sops-nix documentation ↗
- sops-nix module ↗
- sops-nix templates module ↗
- Official ssh-to-age documentation ↗
Adapted from technical notes. Examples use generic names.