Summary
system-manager's sops-nix support works, but only for the default
configuration. Three configurations produce no secrets at all, with no error
and no warning.
nix/modules/upstream/sops-nix.nix declares system.activationScripts.{setupSecrets,setupSecretsForUsers,generate-age-key} as stubs. That is enough for the common case, because sops-nix's sops.useSystemdActivation defaults to config.services.userborn.enable, which system-manager sets to true — so secrets get installed from sops-install-secrets.service and the stubbed activation scripts are never populated.
The three cases below fall outside that.
1. sops.age.generateKey = true
sops-nix only implements key generation as an activation script (system.activationScripts.generate-age-key). The stub absorbs it, so the key is never generated and sops-install-secrets.service fails on any machine that does not already have the key at sops.age.keyFile.
2. sops.secrets.<name>.neededForUsers = true
modules/sops/secrets-for-users/default.nix orders the unit around systemd-sysusers.service:
systemd.services.sops-install-secrets-for-users = {
wantedBy = [ "systemd-sysusers.service" ];
before = [ "systemd-sysusers.service" ];
...
};
system-manager creates users with userborn and sets systemd.sysusers.enable = false. The only symlink emitted for this unit is therefore systemd-sysusers.service.wants/sops-install-secrets-for-users.service; it is not in sysinit-reactivation.target.requires, so it never runs on activation, and it is not ordered before userborn.service, so the secret is not there when the user that consumes it is created.
Reproducer — a user whose password comes from sops ends up without one:
sops.secrets.alice-password.neededForUsers = true;
users.users.alice = {
isNormalUser = true;
hashedPasswordFile = config.sops.secrets.alice-password.path;
};
3. services.userborn.enable = false
This flips sops.useSystemdActivation to false, which switches sops-nix to the activation-script path that system-manager cannot run. Secrets management is disabled entirely, silently.
Worth noting for whoever fixes this: forcing sops.useSystemdActivation = true is enough for regular secrets, but not for neededForUsers ones. secrets-for-users/default.nix recomputes the condition locally and ignores the option:
useSystemdActivation =
sysusersEnabled || (options.services ? userborn && config.services.userborn.enable);
So case 2 remains tied to userborn being enabled, whatever useSystemdActivation says.
4. The documented behaviour does not match any of this
docs/site/how-to/import-nixos-module.md presents the activation-script options as pure stubs and states that "the actual secret decryption is handled differently in system-manager through a systemd service" without saying which service, when it runs, or what enables it. Combined with the CHANGELOG line for 1.1.0 being the only other mention, there is no way for a user to find out that sops-nix works at all, let alone which parts of it do.
Environment
- system-manager
main at 64748b6
- sops-nix
a8627b21b9107c5711c96b84f32a9a4b3d45295f
- Reproduced on Debian 13 and by inspecting the generated units for each case
Expected
The first two should work. The third should either work or fail evaluation with an explanatory message, rather than producing a configuration that looks like it manages secrets and does not.
Fix
I have a fix ready and will open a PR against main shortly. It turns nix/modules/upstream/sops-nix.nix into a compatibility module: it defaults sops.useSystemdActivation to true and asserts it stays enabled, re-points the for-users unit at userborn.service and sysinit-reactivation.target, and runs the generate-age-key script from a oneshot unit. Two container tests cover cases 1 and 2, and the documentation is corrected.
Branch, if useful before the PR is up:
https://github.com/aldoborrero/system-manager/tree/aldoborrero/sops-nix-systemd-activation
One caveat I cannot resolve myself: the two new container tests have not been executed. makeContainerTest requires the uid-range system feature, which the Nix daemon on my machine does not offer and which cannot be toggled from the command line. The same assertions were run and passed as VM tests on Debian 13 before being converted.
Investigated and drafted with the help of Claude Code. The findings above were checked against the repository at the revisions listed in Environment: the quoted sops-nix code, the generated unit files and symlinks, and the test results are all from actual builds and runs, not from model recall. The caveat about the unexecuted container tests is accurate.
Summary
system-manager's sops-nix support works, but only for the default
configuration. Three configurations produce no secrets at all, with no error
and no warning.
nix/modules/upstream/sops-nix.nixdeclaressystem.activationScripts.{setupSecrets,setupSecretsForUsers,generate-age-key}as stubs. That is enough for the common case, because sops-nix'ssops.useSystemdActivationdefaults toconfig.services.userborn.enable, which system-manager sets to true — so secrets get installed fromsops-install-secrets.serviceand the stubbed activation scripts are never populated.The three cases below fall outside that.
1.
sops.age.generateKey = truesops-nix only implements key generation as an activation script (
system.activationScripts.generate-age-key). The stub absorbs it, so the key is never generated andsops-install-secrets.servicefails on any machine that does not already have the key atsops.age.keyFile.2.
sops.secrets.<name>.neededForUsers = truemodules/sops/secrets-for-users/default.nixorders the unit aroundsystemd-sysusers.service:system-manager creates users with userborn and sets
systemd.sysusers.enable = false. The only symlink emitted for this unit is thereforesystemd-sysusers.service.wants/sops-install-secrets-for-users.service; it is not insysinit-reactivation.target.requires, so it never runs on activation, and it is not ordered beforeuserborn.service, so the secret is not there when the user that consumes it is created.Reproducer — a user whose password comes from sops ends up without one:
3.
services.userborn.enable = falseThis flips
sops.useSystemdActivationto false, which switches sops-nix to the activation-script path that system-manager cannot run. Secrets management is disabled entirely, silently.Worth noting for whoever fixes this: forcing
sops.useSystemdActivation = trueis enough for regular secrets, but not forneededForUsersones.secrets-for-users/default.nixrecomputes the condition locally and ignores the option:So case 2 remains tied to userborn being enabled, whatever
useSystemdActivationsays.4. The documented behaviour does not match any of this
docs/site/how-to/import-nixos-module.mdpresents the activation-script options as pure stubs and states that "the actual secret decryption is handled differently in system-manager through a systemd service" without saying which service, when it runs, or what enables it. Combined with theCHANGELOGline for 1.1.0 being the only other mention, there is no way for a user to find out that sops-nix works at all, let alone which parts of it do.Environment
mainat 64748b6a8627b21b9107c5711c96b84f32a9a4b3d45295fExpected
The first two should work. The third should either work or fail evaluation with an explanatory message, rather than producing a configuration that looks like it manages secrets and does not.
Fix
I have a fix ready and will open a PR against
mainshortly. It turnsnix/modules/upstream/sops-nix.nixinto a compatibility module: it defaultssops.useSystemdActivationto true and asserts it stays enabled, re-points the for-users unit atuserborn.serviceandsysinit-reactivation.target, and runs thegenerate-age-keyscript from a oneshot unit. Two container tests cover cases 1 and 2, and the documentation is corrected.Branch, if useful before the PR is up:
https://github.com/aldoborrero/system-manager/tree/aldoborrero/sops-nix-systemd-activation
One caveat I cannot resolve myself: the two new container tests have not been executed.
makeContainerTestrequires theuid-rangesystem feature, which the Nix daemon on my machine does not offer and which cannot be toggled from the command line. The same assertions were run and passed as VM tests on Debian 13 before being converted.Investigated and drafted with the help of Claude Code. The findings above were checked against the repository at the revisions listed in Environment: the quoted sops-nix code, the generated unit files and symlinks, and the test results are all from actual builds and runs, not from model recall. The caveat about the unexecuted container tests is accurate.