at 22.05-pre 24 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 cfg = config.nix; 8 9 nix = cfg.package.out; 10 11 nixVersion = getVersion nix; 12 13 isNix23 = versionAtLeast nixVersion "2.3pre"; 14 15 makeNixBuildUser = nr: { 16 name = "nixbld${toString nr}"; 17 value = { 18 description = "Nix build user ${toString nr}"; 19 20 /* For consistency with the setgid(2), setuid(2), and setgroups(2) 21 calls in `libstore/build.cc', don't add any supplementary group 22 here except "nixbld". */ 23 uid = builtins.add config.ids.uids.nixbld nr; 24 isSystemUser = true; 25 group = "nixbld"; 26 extraGroups = [ "nixbld" ]; 27 }; 28 }; 29 30 nixbldUsers = listToAttrs (map makeNixBuildUser (range 1 cfg.nrBuildUsers)); 31 32 nixConf = 33 assert versionAtLeast nixVersion "2.2"; 34 pkgs.runCommand "nix.conf" { preferLocalBuild = true; extraOptions = cfg.extraOptions; } ( 35 '' 36 cat > $out <<END 37 # WARNING: this file is generated from the nix.* options in 38 # your NixOS configuration, typically 39 # /etc/nixos/configuration.nix. Do not edit it! 40 build-users-group = nixbld 41 max-jobs = ${toString (cfg.maxJobs)} 42 cores = ${toString (cfg.buildCores)} 43 sandbox = ${if (builtins.isBool cfg.useSandbox) then boolToString cfg.useSandbox else cfg.useSandbox} 44 extra-sandbox-paths = ${toString cfg.sandboxPaths} 45 substituters = ${toString cfg.binaryCaches} 46 trusted-substituters = ${toString cfg.trustedBinaryCaches} 47 trusted-public-keys = ${toString cfg.binaryCachePublicKeys} 48 auto-optimise-store = ${boolToString cfg.autoOptimiseStore} 49 require-sigs = ${boolToString cfg.requireSignedBinaryCaches} 50 trusted-users = ${toString cfg.trustedUsers} 51 allowed-users = ${toString cfg.allowedUsers} 52 ${optionalString (!cfg.distributedBuilds) '' 53 builders = 54 ''} 55 system-features = ${toString cfg.systemFeatures} 56 ${optionalString isNix23 '' 57 sandbox-fallback = false 58 ''} 59 $extraOptions 60 END 61 '' + optionalString cfg.checkConfig ( 62 if pkgs.stdenv.hostPlatform != pkgs.stdenv.buildPlatform then '' 63 echo "Ignore nix.checkConfig when cross-compiling" 64 '' else '' 65 echo "Checking that Nix can read nix.conf..." 66 ln -s $out ./nix.conf 67 NIX_CONF_DIR=$PWD ${cfg.package}/bin/nix show-config ${optionalString isNix23 "--no-net --option experimental-features nix-command"} >/dev/null 68 '') 69 ); 70 71in 72 73{ 74 imports = [ 75 (mkRenamedOptionModule [ "nix" "useChroot" ] [ "nix" "useSandbox" ]) 76 (mkRenamedOptionModule [ "nix" "chrootDirs" ] [ "nix" "sandboxPaths" ]) 77 ]; 78 79 ###### interface 80 81 options = { 82 83 nix = { 84 85 enable = mkOption { 86 type = types.bool; 87 default = true; 88 description = '' 89 Whether to enable Nix. 90 Disabling Nix makes the system hard to modify and the Nix programs and configuration will not be made available by NixOS itself. 91 ''; 92 }; 93 94 package = mkOption { 95 type = types.package; 96 default = pkgs.nix; 97 defaultText = literalExpression "pkgs.nix"; 98 description = '' 99 This option specifies the Nix package instance to use throughout the system. 100 ''; 101 }; 102 103 maxJobs = mkOption { 104 type = types.either types.int (types.enum ["auto"]); 105 default = "auto"; 106 example = 64; 107 description = '' 108 This option defines the maximum number of jobs that Nix will try to 109 build in parallel. The default is auto, which means it will use all 110 available logical cores. It is recommend to set it to the total 111 number of logical cores in your system (e.g., 16 for two CPUs with 4 112 cores each and hyper-threading). 113 ''; 114 }; 115 116 autoOptimiseStore = mkOption { 117 type = types.bool; 118 default = false; 119 example = true; 120 description = '' 121 If set to true, Nix automatically detects files in the store that have 122 identical contents, and replaces them with hard links to a single copy. 123 This saves disk space. If set to false (the default), you can still run 124 nix-store --optimise to get rid of duplicate files. 125 ''; 126 }; 127 128 buildCores = mkOption { 129 type = types.int; 130 default = 0; 131 example = 64; 132 description = '' 133 This option defines the maximum number of concurrent tasks during 134 one build. It affects, e.g., -j option for make. 135 The special value 0 means that the builder should use all 136 available CPU cores in the system. Some builds may become 137 non-deterministic with this option; use with care! Packages will 138 only be affected if enableParallelBuilding is set for them. 139 ''; 140 }; 141 142 useSandbox = mkOption { 143 type = types.either types.bool (types.enum ["relaxed"]); 144 default = true; 145 description = " 146 If set, Nix will perform builds in a sandboxed environment that it 147 will set up automatically for each build. This prevents impurities 148 in builds by disallowing access to dependencies outside of the Nix 149 store by using network and mount namespaces in a chroot environment. 150 This is enabled by default even though it has a possible performance 151 impact due to the initial setup time of a sandbox for each build. It 152 doesn't affect derivation hashes, so changing this option will not 153 trigger a rebuild of packages. 154 "; 155 }; 156 157 sandboxPaths = mkOption { 158 type = types.listOf types.str; 159 default = []; 160 example = [ "/dev" "/proc" ]; 161 description = 162 '' 163 Directories from the host filesystem to be included 164 in the sandbox. 165 ''; 166 }; 167 168 extraOptions = mkOption { 169 type = types.lines; 170 default = ""; 171 example = '' 172 keep-outputs = true 173 keep-derivations = true 174 ''; 175 description = "Additional text appended to <filename>nix.conf</filename>."; 176 }; 177 178 distributedBuilds = mkOption { 179 type = types.bool; 180 default = false; 181 description = '' 182 Whether to distribute builds to the machines listed in 183 <option>nix.buildMachines</option>. 184 ''; 185 }; 186 187 daemonCPUSchedPolicy = mkOption { 188 type = types.enum ["other" "batch" "idle"]; 189 default = "other"; 190 example = "batch"; 191 description = '' 192 Nix daemon process CPU scheduling policy. This policy propagates to 193 build processes. other is the default scheduling policy for regular 194 tasks. The batch policy is similar to other, but optimised for 195 non-interactive tasks. idle is for extremely low-priority tasks 196 that should only be run when no other task requires CPU time. 197 198 Please note that while using the idle policy may greatly improve 199 responsiveness of a system performing expensive builds, it may also 200 slow down and potentially starve crucial configuration updates 201 during load. 202 ''; 203 }; 204 205 daemonIOSchedClass = mkOption { 206 type = types.enum ["best-effort" "idle"]; 207 default = "best-effort"; 208 example = "idle"; 209 description = '' 210 Nix daemon process I/O scheduling class. This class propagates to 211 build processes. best-effort is the default class for regular tasks. 212 The idle class is for extremely low-priority tasks that should only 213 perform I/O when no other task does. 214 215 Please note that while using the idle scheduling class can improve 216 responsiveness of a system performing expensive builds, it might also 217 slow down or starve crucial configuration updates during load. 218 ''; 219 }; 220 221 daemonIOSchedPriority = mkOption { 222 type = types.int; 223 default = 0; 224 example = 1; 225 description = '' 226 Nix daemon process I/O scheduling priority. This priority propagates 227 to build processes. The supported priorities depend on the 228 scheduling policy: With idle, priorities are not used in scheduling 229 decisions. best-effort supports values in the range 0 (high) to 7 230 (low). 231 ''; 232 }; 233 234 buildMachines = mkOption { 235 type = types.listOf (types.submodule ({ 236 options = { 237 hostName = mkOption { 238 type = types.str; 239 example = "nixbuilder.example.org"; 240 description = '' 241 The hostname of the build machine. 242 ''; 243 }; 244 system = mkOption { 245 type = types.nullOr types.str; 246 default = null; 247 example = "x86_64-linux"; 248 description = '' 249 The system type the build machine can execute derivations on. 250 Either this attribute or <varname>systems</varname> must be 251 present, where <varname>system</varname> takes precedence if 252 both are set. 253 ''; 254 }; 255 systems = mkOption { 256 type = types.listOf types.str; 257 default = []; 258 example = [ "x86_64-linux" "aarch64-linux" ]; 259 description = '' 260 The system types the build machine can execute derivations on. 261 Either this attribute or <varname>system</varname> must be 262 present, where <varname>system</varname> takes precedence if 263 both are set. 264 ''; 265 }; 266 sshUser = mkOption { 267 type = types.nullOr types.str; 268 default = null; 269 example = "builder"; 270 description = '' 271 The username to log in as on the remote host. This user must be 272 able to log in and run nix commands non-interactively. It must 273 also be privileged to build derivations, so must be included in 274 <option>nix.trustedUsers</option>. 275 ''; 276 }; 277 sshKey = mkOption { 278 type = types.nullOr types.str; 279 default = null; 280 example = "/root/.ssh/id_buildhost_builduser"; 281 description = '' 282 The path to the SSH private key with which to authenticate on 283 the build machine. The private key must not have a passphrase. 284 If null, the building user (root on NixOS machines) must have an 285 appropriate ssh configuration to log in non-interactively. 286 287 Note that for security reasons, this path must point to a file 288 in the local filesystem, *not* to the nix store. 289 ''; 290 }; 291 maxJobs = mkOption { 292 type = types.int; 293 default = 1; 294 description = '' 295 The number of concurrent jobs the build machine supports. The 296 build machine will enforce its own limits, but this allows hydra 297 to schedule better since there is no work-stealing between build 298 machines. 299 ''; 300 }; 301 speedFactor = mkOption { 302 type = types.int; 303 default = 1; 304 description = '' 305 The relative speed of this builder. This is an arbitrary integer 306 that indicates the speed of this builder, relative to other 307 builders. Higher is faster. 308 ''; 309 }; 310 mandatoryFeatures = mkOption { 311 type = types.listOf types.str; 312 default = []; 313 example = [ "big-parallel" ]; 314 description = '' 315 A list of features mandatory for this builder. The builder will 316 be ignored for derivations that don't require all features in 317 this list. All mandatory features are automatically included in 318 <varname>supportedFeatures</varname>. 319 ''; 320 }; 321 supportedFeatures = mkOption { 322 type = types.listOf types.str; 323 default = []; 324 example = [ "kvm" "big-parallel" ]; 325 description = '' 326 A list of features supported by this builder. The builder will 327 be ignored for derivations that require features not in this 328 list. 329 ''; 330 }; 331 }; 332 })); 333 default = []; 334 description = '' 335 This option lists the machines to be used if distributed builds are 336 enabled (see <option>nix.distributedBuilds</option>). 337 Nix will perform derivations on those machines via SSH by copying the 338 inputs to the Nix store on the remote machine, starting the build, 339 then copying the output back to the local Nix store. 340 ''; 341 }; 342 343 # Environment variables for running Nix. 344 envVars = mkOption { 345 type = types.attrs; 346 internal = true; 347 default = {}; 348 description = "Environment variables used by Nix."; 349 }; 350 351 nrBuildUsers = mkOption { 352 type = types.int; 353 description = '' 354 Number of <literal>nixbld</literal> user accounts created to 355 perform secure concurrent builds. If you receive an error 356 message saying that all build users are currently in use, 357 you should increase this value. 358 ''; 359 }; 360 361 readOnlyStore = mkOption { 362 type = types.bool; 363 default = true; 364 description = '' 365 If set, NixOS will enforce the immutability of the Nix store 366 by making <filename>/nix/store</filename> a read-only bind 367 mount. Nix will automatically make the store writable when 368 needed. 369 ''; 370 }; 371 372 binaryCaches = mkOption { 373 type = types.listOf types.str; 374 description = '' 375 List of binary cache URLs used to obtain pre-built binaries 376 of Nix packages. 377 378 By default https://cache.nixos.org/ is added, 379 to override it use <literal>lib.mkForce []</literal>. 380 ''; 381 }; 382 383 trustedBinaryCaches = mkOption { 384 type = types.listOf types.str; 385 default = [ ]; 386 example = [ "https://hydra.nixos.org/" ]; 387 description = '' 388 List of binary cache URLs that non-root users can use (in 389 addition to those specified using 390 <option>nix.binaryCaches</option>) by passing 391 <literal>--option binary-caches</literal> to Nix commands. 392 ''; 393 }; 394 395 requireSignedBinaryCaches = mkOption { 396 type = types.bool; 397 default = true; 398 description = '' 399 If enabled (the default), Nix will only download binaries from binary caches if 400 they are cryptographically signed with any of the keys listed in 401 <option>nix.binaryCachePublicKeys</option>. If disabled, signatures are neither 402 required nor checked, so it's strongly recommended that you use only 403 trustworthy caches and https to prevent man-in-the-middle attacks. 404 ''; 405 }; 406 407 binaryCachePublicKeys = mkOption { 408 type = types.listOf types.str; 409 example = [ "hydra.nixos.org-1:CNHJZBh9K4tP3EKF6FkkgeVYsS3ohTl+oS0Qa8bezVs=" ]; 410 description = '' 411 List of public keys used to sign binary caches. If 412 <option>nix.requireSignedBinaryCaches</option> is enabled, 413 then Nix will use a binary from a binary cache if and only 414 if it is signed by <emphasis>any</emphasis> of the keys 415 listed here. By default, only the key for 416 <uri>cache.nixos.org</uri> is included. 417 ''; 418 }; 419 420 trustedUsers = mkOption { 421 type = types.listOf types.str; 422 default = [ "root" ]; 423 example = [ "root" "alice" "@wheel" ]; 424 description = '' 425 A list of names of users that have additional rights when 426 connecting to the Nix daemon, such as the ability to specify 427 additional binary caches, or to import unsigned NARs. You 428 can also specify groups by prefixing them with 429 <literal>@</literal>; for instance, 430 <literal>@wheel</literal> means all users in the wheel 431 group. 432 ''; 433 }; 434 435 allowedUsers = mkOption { 436 type = types.listOf types.str; 437 default = [ "*" ]; 438 example = [ "@wheel" "@builders" "alice" "bob" ]; 439 description = '' 440 A list of names of users (separated by whitespace) that are 441 allowed to connect to the Nix daemon. As with 442 <option>nix.trustedUsers</option>, you can specify groups by 443 prefixing them with <literal>@</literal>. Also, you can 444 allow all users by specifying <literal>*</literal>. The 445 default is <literal>*</literal>. Note that trusted users are 446 always allowed to connect. 447 ''; 448 }; 449 450 nixPath = mkOption { 451 type = types.listOf types.str; 452 default = 453 [ 454 "nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos" 455 "nixos-config=/etc/nixos/configuration.nix" 456 "/nix/var/nix/profiles/per-user/root/channels" 457 ]; 458 description = '' 459 The default Nix expression search path, used by the Nix 460 evaluator to look up paths enclosed in angle brackets 461 (e.g. <literal>&lt;nixpkgs&gt;</literal>). 462 ''; 463 }; 464 465 systemFeatures = mkOption { 466 type = types.listOf types.str; 467 example = [ "kvm" "big-parallel" "gccarch-skylake" ]; 468 description = '' 469 The supported features of a machine 470 ''; 471 }; 472 473 checkConfig = mkOption { 474 type = types.bool; 475 default = true; 476 description = '' 477 If enabled (the default), checks that Nix can parse the generated nix.conf. 478 ''; 479 }; 480 481 registry = mkOption { 482 type = types.attrsOf (types.submodule ( 483 let 484 inputAttrs = types.attrsOf (types.oneOf [types.str types.int types.bool types.package]); 485 in 486 { config, name, ... }: 487 { options = { 488 from = mkOption { 489 type = inputAttrs; 490 example = { type = "indirect"; id = "nixpkgs"; }; 491 description = "The flake reference to be rewritten."; 492 }; 493 to = mkOption { 494 type = inputAttrs; 495 example = { type = "github"; owner = "my-org"; repo = "my-nixpkgs"; }; 496 description = "The flake reference to which <option>from></option> is to be rewritten."; 497 }; 498 flake = mkOption { 499 type = types.nullOr types.attrs; 500 default = null; 501 example = literalExpression "nixpkgs"; 502 description = '' 503 The flake input to which <option>from></option> is to be rewritten. 504 ''; 505 }; 506 exact = mkOption { 507 type = types.bool; 508 default = true; 509 description = '' 510 Whether the <option>from</option> reference needs to match exactly. If set, 511 a <option>from</option> reference like <literal>nixpkgs</literal> does not 512 match with a reference like <literal>nixpkgs/nixos-20.03</literal>. 513 ''; 514 }; 515 }; 516 config = { 517 from = mkDefault { type = "indirect"; id = name; }; 518 to = mkIf (config.flake != null) 519 ({ type = "path"; 520 path = config.flake.outPath; 521 } // lib.filterAttrs 522 (n: v: n == "lastModified" || n == "rev" || n == "revCount" || n == "narHash") 523 config.flake); 524 }; 525 } 526 )); 527 default = {}; 528 description = '' 529 A system-wide flake registry. 530 ''; 531 }; 532 533 }; 534 535 }; 536 537 538 ###### implementation 539 540 config = mkIf cfg.enable { 541 542 nix.binaryCachePublicKeys = [ "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" ]; 543 nix.binaryCaches = [ "https://cache.nixos.org/" ]; 544 545 environment.systemPackages = 546 [ nix 547 pkgs.nix-info 548 ] 549 ++ optional (config.programs.bash.enableCompletion && !versionAtLeast nixVersion "2.4pre") pkgs.nix-bash-completions; 550 551 environment.etc."nix/nix.conf".source = nixConf; 552 553 environment.etc."nix/registry.json".text = builtins.toJSON { 554 version = 2; 555 flakes = mapAttrsToList (n: v: { inherit (v) from to exact; }) cfg.registry; 556 }; 557 558 # List of machines for distributed Nix builds in the format 559 # expected by build-remote.pl. 560 environment.etc."nix/machines" = 561 { enable = cfg.buildMachines != []; 562 text = 563 concatMapStrings (machine: 564 "${if machine.sshUser != null then "${machine.sshUser}@" else ""}${machine.hostName} " 565 + (if machine.system != null then machine.system else concatStringsSep "," machine.systems) 566 + " ${if machine.sshKey != null then machine.sshKey else "-"} ${toString machine.maxJobs} " 567 + toString (machine.speedFactor) 568 + " " 569 + concatStringsSep "," (machine.mandatoryFeatures ++ machine.supportedFeatures) 570 + " " 571 + concatStringsSep "," machine.mandatoryFeatures 572 + "\n" 573 ) cfg.buildMachines; 574 }; 575 assertions = 576 let badMachine = m: m.system == null && m.systems == []; 577 in [ 578 { 579 assertion = !(builtins.any badMachine cfg.buildMachines); 580 message = '' 581 At least one system type (via <varname>system</varname> or 582 <varname>systems</varname>) must be set for every build machine. 583 Invalid machine specifications: 584 '' + " " + 585 (builtins.concatStringsSep "\n " 586 (builtins.map (m: m.hostName) 587 (builtins.filter (badMachine) cfg.buildMachines))); 588 } 589 ]; 590 591 592 systemd.packages = [ nix ]; 593 594 systemd.sockets.nix-daemon.wantedBy = [ "sockets.target" ]; 595 596 systemd.services.nix-daemon = 597 { path = [ nix pkgs.util-linux config.programs.ssh.package ] 598 ++ optionals cfg.distributedBuilds [ pkgs.gzip ]; 599 600 environment = cfg.envVars 601 // { CURL_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt"; } 602 // config.networking.proxy.envVars; 603 604 unitConfig.RequiresMountsFor = "/nix/store"; 605 606 serviceConfig = 607 { CPUSchedulingPolicy = cfg.daemonCPUSchedPolicy; 608 IOSchedulingClass = cfg.daemonIOSchedClass; 609 IOSchedulingPriority = cfg.daemonIOSchedPriority; 610 LimitNOFILE = 4096; 611 }; 612 613 restartTriggers = [ nixConf ]; 614 }; 615 616 # Set up the environment variables for running Nix. 617 environment.sessionVariables = cfg.envVars // 618 { NIX_PATH = cfg.nixPath; 619 }; 620 621 environment.extraInit = 622 '' 623 if [ -e "$HOME/.nix-defexpr/channels" ]; then 624 export NIX_PATH="$HOME/.nix-defexpr/channels''${NIX_PATH:+:$NIX_PATH}" 625 fi 626 ''; 627 628 nix.nrBuildUsers = mkDefault (lib.max 32 (if cfg.maxJobs == "auto" then 0 else cfg.maxJobs)); 629 630 users.users = nixbldUsers; 631 632 services.xserver.displayManager.hiddenUsers = attrNames nixbldUsers; 633 634 system.activationScripts.nix = stringAfter [ "etc" "users" ] 635 '' 636 install -m 0755 -d /nix/var/nix/{gcroots,profiles}/per-user 637 638 # Subscribe the root user to the NixOS channel by default. 639 if [ ! -e "/root/.nix-channels" ]; then 640 echo "${config.system.defaultChannel} nixos" > "/root/.nix-channels" 641 fi 642 ''; 643 644 nix.systemFeatures = mkDefault ( 645 [ "nixos-test" "benchmark" "big-parallel" "kvm" ] ++ 646 optionals (pkgs.hostPlatform ? gcc.arch) ( 647 # a builder can run code for `gcc.arch` and inferior architectures 648 [ "gccarch-${pkgs.hostPlatform.gcc.arch}" ] ++ 649 map (x: "gccarch-${x}") lib.systems.architectures.inferiors.${pkgs.hostPlatform.gcc.arch} 650 ) 651 ); 652 653 }; 654 655}