From b6f7d1c851e90f540e938db89e934b8b1c5e4ffe Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 8 Apr 2026 13:41:08 +0200 Subject: [PATCH] nixos/tmpfiles: fix layer inversion The nix module uses tmpfiles, not the other way around. Move definitions so that tmpfiles does not depend on the nix module. This makes the tmpfiles module usable without loading a nix module. --- nixos/modules/services/system/nix-daemon.nix | 169 ++++++++++-------- .../modules/system/boot/systemd/tmpfiles.nix | 8 - 2 files changed, 90 insertions(+), 87 deletions(-) diff --git a/nixos/modules/services/system/nix-daemon.nix b/nixos/modules/services/system/nix-daemon.nix index bceaf82798fc..97a5752bd35c 100644 --- a/nixos/modules/services/system/nix-daemon.nix +++ b/nixos/modules/services/system/nix-daemon.nix @@ -297,94 +297,105 @@ in ###### implementation - config = lib.mkIf (cfg.enable && nixPackage.pname != "lix") { - environment.systemPackages = [ - nixPackage - pkgs.nix-info - ] - ++ lib.optional (config.programs.bash.completion.enable) pkgs.nix-bash-completions; - - systemd.packages = [ nixPackage ]; - - # The upstream Nix tmpfiles.d file assumes the daemon runs as root - systemd.tmpfiles.packages = lib.mkIf (cfg.daemonUser == "root") [ nixPackage ]; - - systemd.sockets.nix-daemon.wantedBy = [ "sockets.target" ]; - - systemd.services.nix-daemon = { - path = [ + config = lib.mkMerge [ + (lib.mkIf cfg.enable { + systemd.tmpfiles.rules = [ + "d /nix/var 0755 root root - -" + "L+ /nix/var/nix/gcroots/booted-system 0755 root root - /run/booted-system" + # Boot-time cleanup + "R! /nix/var/nix/gcroots/tmp - - - - -" + "R! /nix/var/nix/temproots - - - - -" + ]; + }) + (lib.mkIf (cfg.enable && nixPackage.pname != "lix") { + environment.systemPackages = [ nixPackage - config.programs.ssh.package + pkgs.nix-info ] - # For running "newuidmap" - ++ lib.optional (cfg.daemonUser != "root") "/run/wrappers"; + ++ lib.optional (config.programs.bash.completion.enable) pkgs.nix-bash-completions; - environment = - cfg.envVars - // { - CURL_CA_BUNDLE = config.security.pki.caBundle; - } - // config.networking.proxy.envVars; + systemd.packages = [ nixPackage ]; + + # The upstream Nix tmpfiles.d file assumes the daemon runs as root + systemd.tmpfiles.packages = lib.mkIf (cfg.daemonUser == "root") [ nixPackage ]; + + systemd.sockets.nix-daemon.wantedBy = [ "sockets.target" ]; + + systemd.services.nix-daemon = { + path = [ + nixPackage + config.programs.ssh.package + ] + # For running "newuidmap" + ++ lib.optional (cfg.daemonUser != "root") "/run/wrappers"; + + environment = + cfg.envVars + // { + CURL_CA_BUNDLE = config.security.pki.caBundle; + } + // config.networking.proxy.envVars; + + serviceConfig = { + CPUSchedulingPolicy = cfg.daemonCPUSchedPolicy; + IOSchedulingClass = cfg.daemonIOSchedClass; + IOSchedulingPriority = cfg.daemonIOSchedPriority; + }; + + restartTriggers = [ config.environment.etc."nix/nix.conf".source ]; + + # `stopIfChanged = false` changes to switch behavior + # from stop -> update units -> start + # to update units -> restart + # + # The `stopIfChanged` setting therefore controls a trade-off between a + # more predictable lifecycle, which runs the correct "version" of + # the `ExecStop` line, and on the other hand the availability of + # sockets during the switch, as the effectiveness of the stop operation + # depends on the socket being stopped as well. + # + # As `nix-daemon.service` does not make use of `ExecStop`, we prefer + # to keep the socket up and available. This is important for machines + # that run Nix-based services, such as automated build, test, and deploy + # services, that expect the daemon socket to be available at all times. + # + # Notably, the Nix client does not retry on failure to connect to the + # daemon socket, and the in-process RemoteStore instance will disable + # itself. This makes retries infeasible even for services that are + # aware of the issue. Failure to connect can affect not only new client + # processes, but also new RemoteStore instances in existing processes, + # as well as existing RemoteStore instances that have not saturated + # their connection pool. + # + # Also note that `stopIfChanged = true` does not kill existing + # connection handling daemons, as one might wish to happen before a + # breaking Nix upgrade (which is rare). The daemon forks that handle + # the individual connections split off into their own sessions, causing + # them not to be stopped by systemd. + # If a Nix upgrade does require all existing daemon processes to stop, + # nix-daemon must do so on its own accord, and only when the new version + # starts and detects that Nix's persistent state needs an upgrade. + stopIfChanged = false; - serviceConfig = { - CPUSchedulingPolicy = cfg.daemonCPUSchedPolicy; - IOSchedulingClass = cfg.daemonIOSchedClass; - IOSchedulingPriority = cfg.daemonIOSchedPriority; }; - restartTriggers = [ config.environment.etc."nix/nix.conf".source ]; + # Set up the environment variables for running Nix. + environment.sessionVariables = cfg.envVars; - # `stopIfChanged = false` changes to switch behavior - # from stop -> update units -> start - # to update units -> restart - # - # The `stopIfChanged` setting therefore controls a trade-off between a - # more predictable lifecycle, which runs the correct "version" of - # the `ExecStop` line, and on the other hand the availability of - # sockets during the switch, as the effectiveness of the stop operation - # depends on the socket being stopped as well. - # - # As `nix-daemon.service` does not make use of `ExecStop`, we prefer - # to keep the socket up and available. This is important for machines - # that run Nix-based services, such as automated build, test, and deploy - # services, that expect the daemon socket to be available at all times. - # - # Notably, the Nix client does not retry on failure to connect to the - # daemon socket, and the in-process RemoteStore instance will disable - # itself. This makes retries infeasible even for services that are - # aware of the issue. Failure to connect can affect not only new client - # processes, but also new RemoteStore instances in existing processes, - # as well as existing RemoteStore instances that have not saturated - # their connection pool. - # - # Also note that `stopIfChanged = true` does not kill existing - # connection handling daemons, as one might wish to happen before a - # breaking Nix upgrade (which is rare). The daemon forks that handle - # the individual connections split off into their own sessions, causing - # them not to be stopped by systemd. - # If a Nix upgrade does require all existing daemon processes to stop, - # nix-daemon must do so on its own accord, and only when the new version - # starts and detects that Nix's persistent state needs an upgrade. - stopIfChanged = false; + nix.nrBuildUsers = lib.mkDefault ( + if cfg.settings.auto-allocate-uids or false then + 0 + else + lib.max 32 (if cfg.settings.max-jobs == "auto" then 0 else cfg.settings.max-jobs) + ); - }; + users.users = nixbldUsers; - # Set up the environment variables for running Nix. - environment.sessionVariables = cfg.envVars; + services.displayManager.hiddenUsers = lib.attrNames nixbldUsers; - nix.nrBuildUsers = lib.mkDefault ( - if cfg.settings.auto-allocate-uids or false then - 0 - else - lib.max 32 (if cfg.settings.max-jobs == "auto" then 0 else cfg.settings.max-jobs) - ); - - users.users = nixbldUsers; - - services.displayManager.hiddenUsers = lib.attrNames nixbldUsers; - - # Legacy configuration conversion. - nix.settings.sandbox-fallback = false; - }; + # Legacy configuration conversion. + nix.settings.sandbox-fallback = false; + }) + ]; } diff --git a/nixos/modules/system/boot/systemd/tmpfiles.nix b/nixos/modules/system/boot/systemd/tmpfiles.nix index 12eca48f945d..c606ff1ac50b 100644 --- a/nixos/modules/system/boot/systemd/tmpfiles.nix +++ b/nixos/modules/system/boot/systemd/tmpfiles.nix @@ -350,19 +350,11 @@ in "d /var/db 0755 root root - -" "L /var/lock - - - - ../run/lock" ] - ++ lib.optionals config.nix.enable [ - "d /nix/var 0755 root root - -" - "L+ /nix/var/nix/gcroots/booted-system 0755 root root - /run/booted-system" - ] # Boot-time cleanup ++ [ "R! /etc/group.lock - - - - -" "R! /etc/passwd.lock - - - - -" "R! /etc/shadow.lock - - - - -" - ] - ++ lib.optionals config.nix.enable [ - "R! /nix/var/nix/gcroots/tmp - - - - -" - "R! /nix/var/nix/temproots - - - - -" ]; boot.initrd.systemd = {