summaryrefslogtreecommitdiff
path: root/system-profiles/nfsroot.nix
blob: a5dce5f263f65f853fbd1b35a976e7b239c10fe9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
{ config, pkgs, lib, flake, flakeInputs, ... }:

with lib;

let
  cfg = config.nfsroot;
in {
  imports = with flake.nixosModules.systemProfiles; [
    tmpfs-root
  ];

  options = {
    nfsroot = {
      storeDevice = mkOption {
        type = types.str;
        default = "nfsroot:nix-store";
      };

      registrationUrl = mkOption {
        type = types.str;
        default = "http://nfsroot/nix-registration";
      };
    };

    system.build = {
      storeContents = mkOption {};
    };
  };

  config = {
    # Don't build the GRUB menu builder script, since we don't need it
    # here and it causes a cyclic dependency.
    boot.loader.grub.enable = false;

    # !!! Hack - attributes expected by other modules.
    environment.systemPackages = [ pkgs.grub2_efi ]
      ++ (if pkgs.stdenv.hostPlatform.system == "aarch64-linux"
          then []
          else [ pkgs.grub2 pkgs.syslinux ]);

    # In stage 1, mount a tmpfs on top of /nix/store (the squashfs
    # image) to make this a live CD.
    fileSystems."/nix/.ro-store" = mkImageMediaOverride
      { fsType = "nfs4";
        device = cfg.storeDevice;
        options = [ "ro" ];
        neededForBoot = true;
      };

    fileSystems."/nix/.rw-store" = mkImageMediaOverride
      { fsType = "tmpfs";
        options = [ "mode=0755" ];
        neededForBoot = true;
      };

    fileSystems."/nix/store" = mkImageMediaOverride
      { fsType = "overlay";
        device = "overlay";
        options = [
          "lowerdir=/nix/.ro-store"
          "upperdir=/nix/.rw-store/store"
          "workdir=/nix/.rw-store/work"
        ];

        depends = [
          "/nix/.ro-store"
          "/nix/.rw-store/store"
          "/nix/.rw-store/work"
        ];
      };

    nix.extraOptions = ''
      use-sqlite-wal = false
    '';

    boot.initrd.availableKernelModules = [ "nfs" "nfsv4" "overlay" ];
    boot.initrd.supportedFilesystems = [ "nfs" "nfsv4" "overlay" ];
    services.rpcbind.enable = mkImageMediaOverride false;

    boot.initrd.network.enable = true;
    boot.initrd.network.flushBeforeStage2 = false; # otherwise nfs doesn't work
    boot.initrd.postMountCommands = ''
      mkdir -p /mnt-root/etc/
      cp /etc/resolv.conf /mnt-root/etc/resolv.conf
    '';
    networking.useDHCP = true;
    networking.resolvconf.enable = false;
    networking.dhcpcd.persistent = true;


    system.build.storeContents = [config.system.build.toplevel];

    system.build.netbootIpxeScript = pkgs.writeTextDir "netboot.ipxe" ''
      #!ipxe
      # Use the cmdline variable to allow the user to specify custom kernel params
      # when chainloading this script from other iPXE scripts like netboot.xyz
      kernel ${pkgs.stdenv.hostPlatform.linux-kernel.target} init=${config.system.build.toplevel}/init initrd=initrd ${toString config.boot.kernelParams} ''${cmdline}
      initrd initrd
      boot
    '';

    boot.postBootCommands =
      ''
        # After booting, register the contents of the Nix store on NFS
        # in the Nix database in the tmpfs.
        ${pkgs.curl}/bin/curl ${escapeShellArg cfg.registrationUrl} | ${config.nix.package.out}/bin/nix-store --load-db

        # nixos-rebuild also requires a "system" profile and an
        # /etc/NIXOS tag.
        touch /etc/NIXOS
        ${config.nix.package}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
      '';
  };
}