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