1/*
2 Declares what makes the nix-daemon work on systemd.
3
4 See also
5 - nixos/modules/config/nix.nix: the nix.conf
6 - nixos/modules/config/nix-remote-build.nix: the nix.conf
7*/
8{ config, lib, pkgs, ... }:
9
10with lib;
11
12let
13
14 cfg = config.nix;
15
16 nixPackage = cfg.package.out;
17
18 isNixAtLeast = versionAtLeast (getVersion nixPackage);
19
20 makeNixBuildUser = nr: {
21 name = "nixbld${toString nr}";
22 value = {
23 description = "Nix build user ${toString nr}";
24
25 /*
26 For consistency with the setgid(2), setuid(2), and setgroups(2)
27 calls in `libstore/build.cc', don't add any supplementary group
28 here except "nixbld".
29 */
30 uid = builtins.add config.ids.uids.nixbld nr;
31 isSystemUser = true;
32 group = "nixbld";
33 extraGroups = [ "nixbld" ];
34 };
35 };
36
37 nixbldUsers = listToAttrs (map makeNixBuildUser (range 1 cfg.nrBuildUsers));
38
39in
40
41{
42 imports = [
43 (mkRenamedOptionModuleWith { sinceRelease = 2205; from = [ "nix" "daemonIONiceLevel" ]; to = [ "nix" "daemonIOSchedPriority" ]; })
44 (mkRenamedOptionModuleWith { sinceRelease = 2211; from = [ "nix" "readOnlyStore" ]; to = [ "boot" "readOnlyNixStore" ]; })
45 (mkRemovedOptionModule [ "nix" "daemonNiceLevel" ] "Consider nix.daemonCPUSchedPolicy instead.")
46 ];
47
48 ###### interface
49
50 options = {
51
52 nix = {
53
54 enable = mkOption {
55 type = types.bool;
56 default = true;
57 description = ''
58 Whether to enable Nix.
59 Disabling Nix makes the system hard to modify and the Nix programs and configuration will not be made available by NixOS itself.
60 '';
61 };
62
63 package = mkOption {
64 type = types.package;
65 default = pkgs.nix;
66 defaultText = literalExpression "pkgs.nix";
67 description = ''
68 This option specifies the Nix package instance to use throughout the system.
69 '';
70 };
71
72 daemonCPUSchedPolicy = mkOption {
73 type = types.enum [ "other" "batch" "idle" ];
74 default = "other";
75 example = "batch";
76 description = ''
77 Nix daemon process CPU scheduling policy. This policy propagates to
78 build processes. `other` is the default scheduling
79 policy for regular tasks. The `batch` policy is
80 similar to `other`, but optimised for
81 non-interactive tasks. `idle` is for extremely
82 low-priority tasks that should only be run when no other task
83 requires CPU time.
84
85 Please note that while using the `idle` policy may
86 greatly improve responsiveness of a system performing expensive
87 builds, it may also slow down and potentially starve crucial
88 configuration updates during load.
89
90 `idle` may therefore be a sensible policy for
91 systems that experience only intermittent phases of high CPU load,
92 such as desktop or portable computers used interactively. Other
93 systems should use the `other` or
94 `batch` policy instead.
95
96 For more fine-grained resource control, please refer to
97 {manpage}`systemd.resource-control(5)` and adjust
98 {option}`systemd.services.nix-daemon` directly.
99 '';
100 };
101
102 daemonIOSchedClass = mkOption {
103 type = types.enum [ "best-effort" "idle" ];
104 default = "best-effort";
105 example = "idle";
106 description = ''
107 Nix daemon process I/O scheduling class. This class propagates to
108 build processes. `best-effort` is the default
109 class for regular tasks. The `idle` class is for
110 extremely low-priority tasks that should only perform I/O when no
111 other task does.
112
113 Please note that while using the `idle` scheduling
114 class can improve responsiveness of a system performing expensive
115 builds, it might also slow down or starve crucial configuration
116 updates during load.
117
118 `idle` may therefore be a sensible class for
119 systems that experience only intermittent phases of high I/O load,
120 such as desktop or portable computers used interactively. Other
121 systems should use the `best-effort` class.
122 '';
123 };
124
125 daemonIOSchedPriority = mkOption {
126 type = types.int;
127 default = 4;
128 example = 1;
129 description = ''
130 Nix daemon process I/O scheduling priority. This priority propagates
131 to build processes. The supported priorities depend on the
132 scheduling policy: With idle, priorities are not used in scheduling
133 decisions. best-effort supports values in the range 0 (high) to 7
134 (low).
135 '';
136 };
137
138 # Environment variables for running Nix.
139 envVars = mkOption {
140 type = types.attrs;
141 internal = true;
142 default = { };
143 description = "Environment variables used by Nix.";
144 };
145
146 nrBuildUsers = mkOption {
147 type = types.int;
148 description = ''
149 Number of `nixbld` user accounts created to
150 perform secure concurrent builds. If you receive an error
151 message saying that “all build users are currently in use”,
152 you should increase this value.
153 '';
154 };
155 };
156 };
157
158
159 ###### implementation
160
161 config = mkIf cfg.enable {
162 environment.systemPackages =
163 [
164 nixPackage
165 pkgs.nix-info
166 ]
167 ++ optional (config.programs.bash.enableCompletion) pkgs.nix-bash-completions;
168
169 systemd.packages = [ nixPackage ];
170
171 systemd.tmpfiles = mkMerge [
172 (mkIf (isNixAtLeast "2.8") {
173 packages = [ nixPackage ];
174 })
175 (mkIf (!isNixAtLeast "2.8") {
176 rules = [
177 "d /nix/var/nix/daemon-socket 0755 root root - -"
178 ];
179 })
180 ];
181
182 systemd.sockets.nix-daemon.wantedBy = [ "sockets.target" ];
183
184 systemd.services.nix-daemon =
185 {
186 path = [ nixPackage pkgs.util-linux config.programs.ssh.package ]
187 ++ optionals cfg.distributedBuilds [ pkgs.gzip ];
188
189 environment = cfg.envVars
190 // { CURL_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt"; }
191 // config.networking.proxy.envVars;
192
193 unitConfig.RequiresMountsFor = "/nix/store";
194
195 serviceConfig =
196 {
197 CPUSchedulingPolicy = cfg.daemonCPUSchedPolicy;
198 IOSchedulingClass = cfg.daemonIOSchedClass;
199 IOSchedulingPriority = cfg.daemonIOSchedPriority;
200 LimitNOFILE = 1048576;
201 };
202
203 restartTriggers = [ config.environment.etc."nix/nix.conf".source ];
204
205 # `stopIfChanged = false` changes to switch behavior
206 # from stop -> update units -> start
207 # to update units -> restart
208 #
209 # The `stopIfChanged` setting therefore controls a trade-off between a
210 # more predictable lifecycle, which runs the correct "version" of
211 # the `ExecStop` line, and on the other hand the availability of
212 # sockets during the switch, as the effectiveness of the stop operation
213 # depends on the socket being stopped as well.
214 #
215 # As `nix-daemon.service` does not make use of `ExecStop`, we prefer
216 # to keep the socket up and available. This is important for machines
217 # that run Nix-based services, such as automated build, test, and deploy
218 # services, that expect the daemon socket to be available at all times.
219 #
220 # Notably, the Nix client does not retry on failure to connect to the
221 # daemon socket, and the in-process RemoteStore instance will disable
222 # itself. This makes retries infeasible even for services that are
223 # aware of the issue. Failure to connect can affect not only new client
224 # processes, but also new RemoteStore instances in existing processes,
225 # as well as existing RemoteStore instances that have not saturated
226 # their connection pool.
227 #
228 # Also note that `stopIfChanged = true` does not kill existing
229 # connection handling daemons, as one might wish to happen before a
230 # breaking Nix upgrade (which is rare). The daemon forks that handle
231 # the individual connections split off into their own sessions, causing
232 # them not to be stopped by systemd.
233 # If a Nix upgrade does require all existing daemon processes to stop,
234 # nix-daemon must do so on its own accord, and only when the new version
235 # starts and detects that Nix's persistent state needs an upgrade.
236 stopIfChanged = false;
237
238 };
239
240 # Set up the environment variables for running Nix.
241 environment.sessionVariables = cfg.envVars;
242
243 nix.nrBuildUsers = mkDefault (
244 if cfg.settings.auto-allocate-uids or false then 0
245 else max 32 (if cfg.settings.max-jobs == "auto" then 0 else cfg.settings.max-jobs)
246 );
247
248 users.users = nixbldUsers;
249
250 services.displayManager.hiddenUsers = attrNames nixbldUsers;
251
252 # Legacy configuration conversion.
253 nix.settings = mkMerge [
254 (mkIf (isNixAtLeast "2.3pre") { sandbox-fallback = false; })
255 ];
256
257 };
258
259}