Secrets Management (sops-nix)
All secrets in this flake are encrypted with sops using age as the backing encryption layer. sops-nix handles decryption at boot and materializes secrets into /run/secrets/ (NixOS) or ~/.config/sops/ (Home Manager).
How it works
secrets/secrets.yaml ──encrypted to──► 15 age public keys (.sops.yaml)
│
▼ sops-nix (at boot)
/run/secrets/<name> (NixOS hosts)
~/.config/sops/<name> (Home Manager / macOS)
Each host derives its age identity from its SSH host ed25519 key. The age public key is listed in .sops.yaml. At boot, sops-nix converts the host’s SSH private key to an age identity and decrypts the secrets.
NixOS hosts
Key generation at first boot
SSH host keys are generated by services.openssh.hostKeys (configured in modules/nixos/services/ssh.nix). The ed25519 key at /etc/ssh/ssh_host_ed25519_key is the one used by sops-nix.
sops-nix auto-converts this key to an age identity via generateKey = true and stores it at /var/lib/sops-nix/keys.txt. No manual key provisioning needed on the host — it “just works” after first boot.
Configuration (per host, implicit)
# modules/nixos/security/secrets.nix
sops = {
defaultSopsFile = "${secretspath}/secrets.yaml";
age = {
sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
keyFile = "/var/lib/sops-nix/keys.txt";
generateKey = true;
};
};
Using a secret in a host config
sops.secrets."tokens/beszel-marvin" = {
mode = "0444";
};
Then reference it:
environment.KEY_FILE = config.sops.secrets."tokens/beszel-marvin".path;
Adding a new NixOS host to secrets
- Generate the age public key from the host’s SSH key:
ssh-to-age < /etc/ssh/ssh_host_ed25519_key.pub
# Output: age1...
If ssh-to-age isn’t available:
nix shell nixpkgs#ssh-to-age --command ssh-to-age < /etc/ssh/ssh_host_ed25519_key.pub
- Add the age public key to
secrets/.sops.yamlunder the host’s entry:
creation_rules:
- path_regex: secrets.yaml$
key_groups:
- age:
- age1... # shinji
- age1... # kenpachi
- age1... # new-host ← add here
- Re-encrypt the secrets file so the new host can decrypt it:
sops updatekeys secrets/secrets.yaml
- Commit the updated
.sops.yamlandsecrets.yaml. The new host can now decrypt secrets on its next boot.
Home Manager / macOS
User age key
Home Manager users decrypt secrets using their own age identity at ~/.config/sops/age/keys.txt (not an SSH host key). This file must be placed manually:
mkdir -p ~/.config/sops/age
# Copy your age identity (private key) here:
echo "AGE-SECRET-KEY-..." > ~/.config/sops/age/keys.txt
chmod 600 ~/.config/sops/age/keys.txt
Bootstrap: user SSH key from secrets
The user’s SSH private key (~/.ssh/id_ed25519) is itself stored in secrets.yaml and decrypted at Home Manager activation. This works because the user’s age key (e.g. mirza) is separately listed in .sops.yaml and can decrypt the SSH key secret.
# modules/home-manager/background/secrets.nix
sops.secrets."ssh_keys/${config.user}" = {
path = "${config.home.homeDirectory}/.ssh/id_ed25519";
};
The user’s age key (for sops CLI use) is separate from the SSH key (for git/SSH access).
Editing secrets
# Decrypt, edit in $EDITOR, re-encrypt
sops secrets/secrets.yaml
To add a new secret entry:
# secrets/secrets.yaml
my-new-token: "sk-abc123..."
Then reference it in your Nix config:
sops.secrets."my-new-token".path = "/run/secrets/my-token";
Key hierarchy
| Key | Source | Used by |
|---|---|---|
| Host age keys (shinji, kenpachi, …) | /etc/ssh/ssh_host_ed25519_key → age | sops-nix decryption on that host |
| User age keys (mirza, mar) | Manually generated with age-keygen | sops CLI for editing secrets |
| User SSH keys | Decrypted from secrets.yaml by Home Manager | Git push, SSH access, etc. |
| SSH host keys | Generated at first boot by services.openssh.hostKeys | SSH server identity + age identity source |
Common commands
# Edit secrets
sops secrets/secrets.yaml
# Re-encrypt after adding/removing recipients
sops updatekeys secrets/secrets.yaml
# Check which keys can decrypt
sops --decrypt secrets/secrets.yaml > /dev/null && echo "OK"
# List recipients
grep age secrets/.sops.yaml
# Convert SSH key to age
ssh-to-age < /etc/ssh/ssh_host_ed25519_key.pub
# Generate a new age key (for users)
age-keygen -o ~/.config/sops/age/keys.txt