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 ] ++ lib.optional (config.programs.bash.completion.enable) pkgs.nix-bash-completions;
195
196 systemd.packages = [ nixPackage ];
197
198 systemd.tmpfiles = lib.mkMerge [
199 (lib.mkIf (isNixAtLeast "2.8") {
200 packages = [ nixPackage ];
201 })
202 (lib.mkIf (!isNixAtLeast "2.8") {
203 rules = [
204 "d /nix/var/nix/daemon-socket 0755 root root - -"
205 ];
206 })
207 ];
208
209 systemd.sockets.nix-daemon.wantedBy = [ "sockets.target" ];
210
211 systemd.services.nix-daemon = {
212 path = [
213 nixPackage
214 pkgs.util-linux
215 config.programs.ssh.package
216 ] ++ lib.optionals cfg.distributedBuilds [ pkgs.gzip ];
217
218 environment =
219 cfg.envVars
220 // {
221 CURL_CA_BUNDLE = config.security.pki.caBundle;
222 }
223 // config.networking.proxy.envVars;
224
225 unitConfig.RequiresMountsFor = "/nix/store";
226
227 serviceConfig = {
228 CPUSchedulingPolicy = cfg.daemonCPUSchedPolicy;
229 IOSchedulingClass = cfg.daemonIOSchedClass;
230 IOSchedulingPriority = cfg.daemonIOSchedPriority;
231 LimitNOFILE = 1048576;
232 Delegate = "yes";
233 };
234
235 restartTriggers = [ config.environment.etc."nix/nix.conf".source ];
236
237 # `stopIfChanged = false` changes to switch behavior
238 # from stop -> update units -> start
239 # to update units -> restart
240 #
241 # The `stopIfChanged` setting therefore controls a trade-off between a
242 # more predictable lifecycle, which runs the correct "version" of
243 # the `ExecStop` line, and on the other hand the availability of
244 # sockets during the switch, as the effectiveness of the stop operation
245 # depends on the socket being stopped as well.
246 #
247 # As `nix-daemon.service` does not make use of `ExecStop`, we prefer
248 # to keep the socket up and available. This is important for machines
249 # that run Nix-based services, such as automated build, test, and deploy
250 # services, that expect the daemon socket to be available at all times.
251 #
252 # Notably, the Nix client does not retry on failure to connect to the
253 # daemon socket, and the in-process RemoteStore instance will disable
254 # itself. This makes retries infeasible even for services that are
255 # aware of the issue. Failure to connect can affect not only new client
256 # processes, but also new RemoteStore instances in existing processes,
257 # as well as existing RemoteStore instances that have not saturated
258 # their connection pool.
259 #
260 # Also note that `stopIfChanged = true` does not kill existing
261 # connection handling daemons, as one might wish to happen before a
262 # breaking Nix upgrade (which is rare). The daemon forks that handle
263 # the individual connections split off into their own sessions, causing
264 # them not to be stopped by systemd.
265 # If a Nix upgrade does require all existing daemon processes to stop,
266 # nix-daemon must do so on its own accord, and only when the new version
267 # starts and detects that Nix's persistent state needs an upgrade.
268 stopIfChanged = false;
269
270 };
271
272 # Set up the environment variables for running Nix.
273 environment.sessionVariables = cfg.envVars;
274
275 nix.nrBuildUsers = lib.mkDefault (
276 if cfg.settings.auto-allocate-uids or false then
277 0
278 else
279 lib.max 32 (if cfg.settings.max-jobs == "auto" then 0 else cfg.settings.max-jobs)
280 );
281
282 users.users = nixbldUsers;
283
284 services.displayManager.hiddenUsers = lib.attrNames nixbldUsers;
285
286 # Legacy configuration conversion.
287 nix.settings = lib.mkMerge [
288 (lib.mkIf (isNixAtLeast "2.3pre") { sandbox-fallback = false; })
289 ];
290
291 };
292
293}