Agenix Secrets
Create age-encrypted secrets and wire them into NixOS modules.
Repo Secret Layout
hosts/
hosts//secrets/secrets.nix
let edmundmiller = "ssh-ed25519 AAAAC3..." ; nuc = "ssh-ed25519 AAAAC3..." ; in { "my-secret.age" . publicKeys = [ edmundmiller nuc ] ; } Both user and host keys are needed — user key to encrypt/edit, host key to decrypt on deploy. 2. Create the encrypted file cd hosts/ < host
/secrets
Pipe content (non-interactive)
printf 'SECRET_VALUE' | age \ -r "ssh-ed25519 AAAA...user" \ -r "ssh-ed25519 AAAA...host" \ -o my-secret.age
Verify decryption
age -d -i ~/.ssh/id_ed25519 my-secret.age The agenix -e CLI requires an interactive editor. For agent workflows, use age directly with -r for each recipient public key from secrets.nix . 3. Reference in NixOS module
Simple: file path reference (most common)
services . myapp . environmentFile = config . age . secrets . my - secret . path ;
Override owner when service runs as different user
age . secrets . my - secret = { owner = "myapp" ; group = "myapp" ; } ; 4. Deploy
Stage by directory — NOT by filename (staging a .age path directly is blocked by the sandbox)
git add hosts/ < host
/secrets/ git commit -m "secrets: add my-secret" git push && hey nuc Sandbox note: The pi sandbox blocks git add (and any bash command) that explicitly references a .age file path. Staging the parent directory avoids this — git add hosts/
/secrets/ stages all changes in the dir without naming .age files directly. Pattern: HA secrets.yaml via !secret Home Assistant's YAML supports !secret key references. The nixpkgs HA module post-processes generated YAML to unquote ! tags (sed converts '!secret foo' → !secret foo ).
In HA module config:
services . home - assistant . config . homeassistant = { latitude = "!secret latitude" ;
Unquoted by nixpkgs sed post-processor
longitude
"!secret longitude" ; } ;
Decrypt with correct owner and symlink into HA config dir:
age . secrets . hass - secrets = { owner = "hass" ; group = "hass" ; } ; systemd . tmpfiles . settings . "10-hass-nix-yaml" = { " $ { config . services . home - assistant . configDir } /secrets.yaml" = { L . argument = config . age . secrets . hass - secrets . path ; } ; } ; The .age file contains standard HA secrets.yaml format: latitude : 33.083423 longitude : -96.820367 Pattern: Update Existing .age File Decrypt → modify → re-encrypt. Common when adding vars to an existing env file.
Decrypt to temp
age -d -i ~/.ssh/id_ed25519 hosts/ < host
/secrets/my-env.age
/tmp/my-env.txt
Modify
echo "NEW_VAR=value"
/tmp/my-env.txt
Re-encrypt (overwrites existing .age)
age \ -r "ssh-ed25519 AAAA...user" \ -r "ssh-ed25519 AAAA...host" \ -o hosts/ < host
/secrets/my-env.age /tmp/my-env.txt
Clean up
rm /tmp/my-env.txt
Verify
age -d -i ~/.ssh/id_ed25519 hosts/ < host
/secrets/my-env.age Pattern: 1Password + Agenix (Login Credentials) For services needing a username/password — store in both 1Password (human access) and agenix (machine access).
1. Generate creds and store in 1Password
PASSWORD
$( op item create \ --category = login \ --title = "MyService" \ --vault = "Private" \ --url = "http://nuc:8080" \ --generate-password = "32,letters,digits" \ username = "emiller" \ --format = json | jq -r '.fields[] | select(.id == "password") | .value' )
2. Create agenix env file
printf 'MYSERVICE_USER=emiller\nMYSERVICE_PASSWORD=%s' " $PASSWORD " | age \ -r "ssh-ed25519 AAAA...user" \ -r "ssh-ed25519 AAAA...host" \ -o hosts/ < host
/secrets/myservice-env.age
3. Add to secrets.nix, wire environmentFile, set owner (see below)
Pattern: Service Environment File with Owner Override Most NixOS services run as a dedicated user (not emiller ). Override the secret owner so the service can read it.
In host config (e.g., hosts/nuc/default.nix):
modules . services . myservice = { enable = true ; environmentFile = config . age . secrets . myservice - env . path ; } ;
Override default owner (emiller) → service user
age
.
secrets
.
myservice
-
env
.
owner
=
"myservice"
;
The service username typically matches the service name. Check with
grep -r "DynamicUser|User=" /etc/systemd/system/