mirror of
https://github.com/NixOS/nixpkgs.git
synced 2026-10-03 21:40:12 +00:00
345 lines
12 KiB
Nix
345 lines
12 KiB
Nix
/*
|
|
Declares what makes the nix-daemon work on systemd.
|
|
See also
|
|
- nixos/modules/config/nix.nix: the nix.conf
|
|
- nixos/modules/config/nix-remote-build.nix: the nix.conf
|
|
*/
|
|
{
|
|
config,
|
|
lib,
|
|
...
|
|
}:
|
|
let
|
|
|
|
cfg = config.nix;
|
|
|
|
nixPackage = cfg.package.out;
|
|
|
|
in
|
|
|
|
{
|
|
imports = [
|
|
(lib.mkRenamedOptionModuleWith {
|
|
sinceRelease = 2205;
|
|
from = [
|
|
"nix"
|
|
"daemonIONiceLevel"
|
|
];
|
|
to = [
|
|
"nix"
|
|
"daemonIOSchedPriority"
|
|
];
|
|
})
|
|
(lib.mkRenamedOptionModuleWith {
|
|
sinceRelease = 2211;
|
|
from = [
|
|
"nix"
|
|
"readOnlyStore"
|
|
];
|
|
to = [
|
|
"boot"
|
|
"readOnlyNixStore"
|
|
];
|
|
})
|
|
(lib.mkRemovedOptionModule [ "nix" "daemonNiceLevel" ] "Consider nix.daemonCPUSchedPolicy instead.")
|
|
{
|
|
# Unprivileged Nix daemon
|
|
config = lib.mkIf (cfg.daemonUser != "root") {
|
|
assertions = [
|
|
{
|
|
message = ''
|
|
The Nix daemon cannot run as the root group when not running as the root user.
|
|
'';
|
|
assertion = cfg.daemonGroup != "root";
|
|
}
|
|
{
|
|
message = ''
|
|
Nix must have the `local-overlay-store` experimental feature when not running as the root user.
|
|
'';
|
|
assertion = lib.elem "local-overlay-store" cfg.settings.experimental-features;
|
|
}
|
|
{
|
|
message = ''
|
|
Nix must have the `auto-allocate-uids` experimental feature when not running as the root user.
|
|
'';
|
|
assertion = lib.elem "auto-allocate-uids" cfg.settings.experimental-features;
|
|
}
|
|
];
|
|
|
|
nix.settings = {
|
|
sandbox = true;
|
|
|
|
auto-allocate-uids = true;
|
|
|
|
# No such group would exist within the sandbox, so chowning to it would fail
|
|
build-users-group = "";
|
|
|
|
# Default settings from Nix, we need to specify them here to use them in nix code though
|
|
start-id = lib.mkDefault (832 * 1024 * 1024);
|
|
id-count = lib.mkDefault (128 * 65536);
|
|
};
|
|
|
|
systemd.services.nix-daemon = {
|
|
# Nix assumes it should use `daemon` if it isn't root, so we have to set `NIX_REMOTE` anyway
|
|
environment.NIX_REMOTE = "local?use-roots-daemon=true";
|
|
# Nix wants a HOME it can access to cache substituter contents, among other things.
|
|
environment.HOME = "%S/nix-daemon";
|
|
serviceConfig = {
|
|
User = cfg.daemonUser;
|
|
Group = cfg.daemonGroup;
|
|
|
|
StateDirectory = "nix-daemon";
|
|
|
|
# Empty string needed to disable old Exec
|
|
ExecStart = [
|
|
""
|
|
"${nixPackage}/libexec/nix-nswrapper ${toString cfg.settings.start-id} ${toString cfg.settings.id-count} ${nixPackage}/bin/nix-daemon --daemon"
|
|
];
|
|
};
|
|
};
|
|
|
|
# We can't remount rw while unprivileged
|
|
boot.nixStoreMountOpts = [
|
|
"nodev"
|
|
"nosuid"
|
|
];
|
|
|
|
users.users."${cfg.daemonUser}" = {
|
|
subUidRanges = [
|
|
{
|
|
startUid = cfg.settings.start-id;
|
|
count = cfg.settings.id-count;
|
|
}
|
|
];
|
|
subGidRanges = [
|
|
{
|
|
startGid = cfg.settings.start-id;
|
|
count = cfg.settings.id-count;
|
|
}
|
|
];
|
|
};
|
|
|
|
systemd.tmpfiles.rules = [
|
|
"d /nix/store 0755 ${config.nix.daemonUser} ${config.nix.daemonGroup} - -"
|
|
"Z /nix/var 0755 ${config.nix.daemonUser} ${config.nix.daemonGroup} - -"
|
|
"d /nix/var/nix/builds 0755 ${config.nix.daemonUser} ${config.nix.daemonGroup} 7d -"
|
|
"d /nix/var/nix/daemon-socket 0755 ${config.nix.daemonUser} ${config.nix.daemonGroup} - -"
|
|
"d /nix/var/nix/gc-roots-socket 0755 ${config.nix.daemonUser} ${config.nix.daemonGroup} - -"
|
|
];
|
|
|
|
systemd.services.nix-roots-daemon = {
|
|
serviceConfig.ExecStart = "${config.nix.package.out}/bin/nix --extra-experimental-features nix-command store roots-daemon";
|
|
};
|
|
systemd.sockets.nix-roots-daemon = {
|
|
wantedBy = [
|
|
"nix-daemon.service"
|
|
];
|
|
listenStreams = [ "/nix/var/nix/gc-roots-socket/socket" ];
|
|
unitConfig = {
|
|
ConditionPathIsReadWrite = "/nix/var/nix/gc-roots-socket";
|
|
RequiresMountsFor = "/nix/store";
|
|
};
|
|
};
|
|
};
|
|
}
|
|
];
|
|
|
|
###### interface
|
|
|
|
options = {
|
|
|
|
nix = {
|
|
|
|
daemon.enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = config.nix.enable;
|
|
defaultText = lib.literalExpression "config.nix.enable";
|
|
description = ''
|
|
Whether to enable the Nix Daemon.
|
|
'';
|
|
};
|
|
|
|
daemonUser = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "root";
|
|
description = ''
|
|
User to use to run the Nix daemon.
|
|
If this is not "root" then the Nix daemon will set several settings to preserve functionality.
|
|
When setting this option, you must also set `nix.daemonGroup`.
|
|
'';
|
|
};
|
|
|
|
daemonGroup = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "root";
|
|
description = ''
|
|
Group to use to run the Nix daemon.
|
|
'';
|
|
};
|
|
|
|
daemonCPUSchedPolicy = lib.mkOption {
|
|
type = lib.types.enum [
|
|
"other"
|
|
"batch"
|
|
"idle"
|
|
];
|
|
default = "other";
|
|
example = "batch";
|
|
description = ''
|
|
Nix daemon process CPU scheduling policy. This policy propagates to
|
|
build processes. `other` is the default scheduling
|
|
policy for regular tasks. The `batch` policy is
|
|
similar to `other`, but optimised for
|
|
non-interactive tasks. `idle` is for extremely
|
|
low-priority tasks that should only be run when no other task
|
|
requires CPU time.
|
|
|
|
Please note that while using the `idle` policy may
|
|
greatly improve responsiveness of a system performing expensive
|
|
builds, it may also slow down and potentially starve crucial
|
|
configuration updates during load.
|
|
|
|
`idle` may therefore be a sensible policy for
|
|
systems that experience only intermittent phases of high CPU load,
|
|
such as desktop or portable computers used interactively. Other
|
|
systems should use the `other` or
|
|
`batch` policy instead.
|
|
|
|
For more fine-grained resource control, please refer to
|
|
{manpage}`systemd.resource-control(5)` and adjust
|
|
{option}`systemd.services.nix-daemon` directly.
|
|
'';
|
|
};
|
|
|
|
daemonIOSchedClass = lib.mkOption {
|
|
type = lib.types.enum [
|
|
"best-effort"
|
|
"idle"
|
|
];
|
|
default = "best-effort";
|
|
example = "idle";
|
|
description = ''
|
|
Nix daemon process I/O scheduling class. This class propagates to
|
|
build processes. `best-effort` is the default
|
|
class for regular tasks. The `idle` class is for
|
|
extremely low-priority tasks that should only perform I/O when no
|
|
other task does.
|
|
|
|
Please note that while using the `idle` scheduling
|
|
class can improve responsiveness of a system performing expensive
|
|
builds, it might also slow down or starve crucial configuration
|
|
updates during load.
|
|
|
|
`idle` may therefore be a sensible class for
|
|
systems that experience only intermittent phases of high I/O load,
|
|
such as desktop or portable computers used interactively. Other
|
|
systems should use the `best-effort` class.
|
|
'';
|
|
};
|
|
|
|
daemonIOSchedPriority = lib.mkOption {
|
|
type = lib.types.int;
|
|
default = 4;
|
|
example = 1;
|
|
description = ''
|
|
Nix daemon process I/O scheduling priority. This priority propagates
|
|
to build processes. The supported priorities depend on the
|
|
scheduling policy: With idle, priorities are not used in scheduling
|
|
decisions. best-effort supports values in the range 0 (high) to 7
|
|
(low).
|
|
'';
|
|
};
|
|
|
|
# Environment variables for running Nix.
|
|
envVars = lib.mkOption {
|
|
type = lib.types.attrs;
|
|
internal = true;
|
|
default = { };
|
|
description = "Environment variables used by Nix.";
|
|
};
|
|
};
|
|
};
|
|
|
|
###### implementation
|
|
|
|
config = lib.mkIf (cfg.daemon.enable && nixPackage.pname != "lix") {
|
|
assertions = [
|
|
{
|
|
assertion = cfg.enable;
|
|
message = ''
|
|
Enabling the Nix Daemon requires also enabling Nix (config.nix.enable = true).
|
|
'';
|
|
}
|
|
];
|
|
|
|
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;
|
|
|
|
};
|
|
|
|
# Set up the environment variables for running Nix.
|
|
environment.sessionVariables = cfg.envVars;
|
|
};
|
|
|
|
}
|