Manage secrets with sops-nix¶
sops-nix decrypts secrets that are committed to your repository in encrypted form and installs them, at runtime, into a ramfs under /run/secrets.
System Manager supports it, so encrypted secrets can live in Git while the plaintext never touches disk.
How it works here¶
On NixOS, sops-nix normally installs secrets from an activation script.
System Manager does not run NixOS activation scripts: it activates a configuration by writing systemd units and restarting sysinit-reactivation.target.
Instead, System Manager uses the systemd code path that sops-nix already provides (sops.useSystemdActivation), which installs secrets from a sops-install-secrets.service oneshot unit.
This is enabled by default and is ordered so that secrets are installed both on boot and on every system-manager switch.
Basic usage¶
Add sops-nix as a flake input and import its NixOS module:
The secret is decrypted into /run/secrets/my-service-env, owned by root with mode 0400 by default.
Choosing a decryption key¶
Because System Manager does not manage services.openssh, sops-nix cannot auto-detect SSH host keys.
Pick one of:
- An age key file — set
sops.age.keyFileto a path outside the Nix store, and provision the key out of band. - An SSH host key — set
sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];explicitly to reuse a key the machine already has. - A generated age key — set
sops.age.generateKey = true;together withsops.age.keyFile. System Manager generates the key from asops-generate-age-key.serviceoneshot ordered before secret installation. The key is only generated if the file does not exist yet, so you have to add its public half to your.sops.yamland re-encrypt before secrets can be decrypted on that machine.
Restarting services when a secret changes¶
sops.secrets.<name>.restartUnits and reloadUnits work as they do on NixOS: when the decrypted value changes, sops-install-secrets acts on the listed units through systemctl directly, rather than through the NixOS activation-restart lists that System Manager does not have.
Two details are worth knowing, because they follow from how the unit is ordered:
- The verbs used are
try-restartandtry-reload-or-restart, so a unit that is currently stopped is left stopped. On first activation that is the right behaviour, since the unit starts with the new secret anyway. - The calls are made with
--no-block.sops-install-secrets.serviceruns beforesysinit-reactivation.targetwithDefaultDependencies=no, and waiting on a regular service (which is orderedAfter=sysinit.target) would deadlock the transaction. So the restart is requested, not awaited.
Secrets needed to create users¶
Secrets marked neededForUsers are installed into /run/secrets-for-users before users are created, which is how you can source a user's password hash from sops:
This requires services.userborn.enable (the default), since userborn is what creates the users.
Secret file ownership¶
Secrets are written into a ramfs at /run/secrets.d, owned by root and by the keys group where it exists. Debian and Ubuntu have no keys group, so sops-install-secrets falls back to nogroup. This affects the mount point only; individual secrets still default to 0400 root-owned, and sops.secrets.<name>.owner/group work as usual.
Limitations¶
sops.useSystemdActivationcannot be disabled: the activation-script path it falls back to does not exist in System Manager. Setting it tofalsefails evaluation with an explanatory error.- Secrets live in
/runand are re-installed on every boot and every activation. A machine that cannot reach its decryption key will failsops-install-secrets.servicerather than start services with stale secrets.
See also¶
- Import a NixOS module for the technique behind the sops-nix compatibility module
- Users and groups example