Skip to content
LogoLogo

Secrets & agenix

cattery integrates agenix for encrypted secret management. Every service module that needs secrets has a companion *.secrets sub-module that auto-wires encrypted config files.

Architecture

secrets/
├── hosts/<host>/global/       # host-specific, root-owned (e.g., nginx conf.d)
├── hosts/<host>/users/<user>/ # host-specific, user-owned (e.g., git config)
├── shared/global/              # shared across all hosts, root-owned
└── shared/users/<user>/       # shared across all hosts, user-owned

Secrets are encrypted with age and can be decrypted with SSH keys or YubiKeys.

Enabling Secrets

{
  cattery.secrets.enable = true;
 
  # Optional: YubiKey support
  cattery.secrets.yubikey.enable = true;
}

Service Secrets

Every service that supports secrets has a secrets sub-module. It auto-enables when both the parent service and cattery.secrets are enabled:

cattery = {
  secrets.enable = true;
 
  # This auto-wires encrypted config files:
  services.nginx.enable = true;     # → encrypted nginx conf.d
  services.forgejo.enable = true;   # → encrypted forgejo app.ini
  services.postgresql.enable = true;# → encrypted postgresql.conf
  services.wg-quick.enable = true;  # → encrypted wireguard configs
  services.cloudflared.enable = true; # → encrypted tunnel credentials
};

Available Secret Modules

ModuleSecret Files
cattery.secretsMaster secrets config (hosts, shared, files)
cattery.nix.secretsEncrypted nix config (!include in nix.extraOptions)
cattery.services.nginx.secretsnginx conf.d files
cattery.services.acme.secretsACME DNS provider env files
cattery.services.forgejo.secretsforgejo app.ini
cattery.services.gitea.secretsgitea app.ini
cattery.services.gitea-actions-runner.secretsRunner token env files
cattery.services.postgresql.secretspostgresql.conf, pg_hba.conf, pg_ident.conf
cattery.services.vaultwarden.secretsvaultwarden env file
cattery.services.wg-quick.secretsWireGuard config files
cattery.services.cloudflared.secretsTunnel credential files
cattery.services.sing-box.secretssing-box config.json
cattery.system.fileSystems.samba.secretsSamba credential files
cattery.cli-apps.ssh.secretsSSH known_hosts files
cattery.cli-apps.security.gnupg.secretsGPG keyring files
cattery.cli-apps.dev-kit.git.secretsGit include configs

Secret Scopes

Each secret is categorized by scope:

ScopePath patternOwnerUse case
hosts-globalhosts/<host>/global/rootSystem-level configs (nginx, postgresql, forgejo)
hosts-userhosts/<host>/users/<user>/userPer-user configs on specific hosts (git, GPG, SSH)
shared-globalshared/global/rootSystem configs shared across all hosts
shared-usershared/users/<user>/userUser configs shared across all hosts

Example: Setting Up Secrets

  1. Create your age key (once per host/user):
mkdir -p ~/.config/sops/age
nix run github:ryantm/agenix -- -e ~/.config/sops/age/keys.txt
  1. Create a .age file with public keys:
# secrets.nix
{
  "hosts/server/global/nginx.conf.age".publicKeys = [ alice server ];
}
  1. Encrypt your secret:
nix run github:ryantm/agenix -- -e secrets/hosts/server/global/nginx.conf.age
  1. Enable the service — the secret is auto-mounted:
cattery = {
  secrets.enable = true;
  services.nginx.enable = true;
};

The encrypted file is decrypted at boot and mounted at its target path (e.g., /etc/nginx/conf.d/nginx.conf).

YubiKey Support

Enable YubiKey-based decryption:

cattery.secrets.yubikey.enable = true;

This configures agenix to use YubiKey identities. Requires a YubiKey with an age-compatible PIV key.

Impermanence + Secrets

When using impermanence (ephemeral root), secrets are automatically persisted. agenix decrypts at boot and the decrypted files are stored on the persistent volume.

cattery = {
  secrets.enable = true;
  system.impermanence.enable = true;
};