This repository has no description
5.3 kB
125 lines
1{
2 modulesPath,
3 pkgs,
4 lib,
5 ...
6}:
7{
8 imports = [
9 ./config.nix
10 (modulesPath + "/installer/scan/not-detected.nix")
11 # Two boot media from one config. Every entry in image.modules is applied
12 # to this config through its own extendModules, so the iso and netboot
13 # variants never see each other's fileSystems / bootloader definitions —
14 # no disabledModules or mkForce juggling needed.
15 #
16 # nixos-rebuild build-image --flake .#atlas --image-variant iso
17 # nix build .#nixosConfigurations.atlas.config.system.build.images.iso
18 # nix build .#atlas-netboot-root (kernel+initrd+ipxe for PXE)
19 (modulesPath + "/image/images.nix")
20 ];
21
22 # Deliberately no image module at the toplevel: nixpkgs warns that doing so
23 # defines system.build.image and leaks the medium into every variant. That
24 # leaves the base config without a real boot medium, so these two
25 # placeholders exist only to let it evaluate; both image variants override
26 # the root with mkImageMediaOverride (priority 60), which wins over these.
27 # The base toplevel itself is never booted or deployed.
28 fileSystems."/" = {
29 device = "tmpfs";
30 fsType = "tmpfs";
31 };
32 boot.loader.grub.enable = lib.mkDefault false;
33
34 # Live USB stick: store is a squashfs on the stick, root a tmpfs overlay on
35 # top. Stateless, nothing installed — dd it and boot.
36 #
37 # The built-in `iso` variant is iso-image.nix (not installation-cd-base):
38 # no nixos channel, no installer profile, no `nixos` autologin to fight
39 # greetd or clash with 2configs/pinned-registry.nix. Settings here merge
40 # into it, since image.modules is an attrsOf deferredModule.
41 image.modules.iso = {
42 # zstd keeps the squashfs small without a slow xz build.
43 isoImage.squashfsCompression = "zstd -Xcompression-level 6";
44 isoImage.makeEfiBootable = true;
45 isoImage.makeUsbBootable = true;
46 image.baseName = lib.mkForce "atlas";
47 image.fileName = lib.mkForce "atlas.iso";
48 };
49
50 # PXE/netboot: iPXE pulls bzImage + initrd, so nothing has to be flashed to
51 # iterate. The whole store rides inside the initrd (~3.8 GB), which is why
52 # it has to be served over HTTP rather than TFTP — see nat-share --pxe/--http.
53 image.modules.netboot = {
54 imports = [ (modulesPath + "/installer/netboot/netboot.nix") ];
55 netboot.squashfsCompression = "zstd -Xcompression-level 6";
56 };
57
58 nixpkgs.hostPlatform = "x86_64-linux";
59
60 boot.kernelPackages = pkgs.linuxPackages_latest;
61
62 # AMD Radeon (Navi 14 / RX 5500-class, PCI 1002:7340) -> amdgpu.
63 #
64 # amdgpu is deliberately NOT in boot.initrd.kernelModules: early-loading it
65 # tears down efifb before the GPU firmware is available in stage-1, which
66 # black-screens this card from the moment the kernel starts (measured: ~5min
67 # to reach systemd). Let udev load amdgpu in stage-2, where the firmware is
68 # present. hardware.amdgpu.initrd.enable is the same trap — it only adds the
69 # module and handles no firmware.
70 hardware.graphics.enable = true;
71 hardware.enableRedistributableFirmware = true;
72
73 # No 32-bit graphics stack: it drags in a second mesa plus a second LLVM
74 # (~840 MB uncompressed) and is only needed for Steam/Wine, which this
75 # stateless box doesn't run. Both image variants pay for it otherwise, and
76 # the netboot initrd has to fit under iPXE's sub-4GB allocation limit.
77 hardware.graphics.enable32Bit = false;
78
79 # nixpkgs' graphical-desktop.nix turns on speech-dispatcher for any
80 # graphical session, which pulls espeak and 676 MB of mbrola TTS voices.
81 # Nothing here speaks.
82 services.speechd.enable = lib.mkForce false;
83 services.xserver.videoDrivers = [ "amdgpu" ];
84 boot.kernelModules = [ "kvm-intel" ];
85
86 # Boot the live image off a USB mass-storage device. (Inert for netboot,
87 # where iPXE has already fetched kernel+initrd before the kernel starts.)
88 boot.initrd.availableKernelModules = [
89 "xhci_pci"
90 "ehci_pci"
91 "ahci"
92 "usb_storage"
93 "uas"
94 "usbhid"
95 "sd_mod"
96 ];
97
98 # Initrd size dominates boot for both media: GRUB reads it off USB at
99 # roughly 1 MB/s (measured: 59s loader phase vs 6.5s kernel + 4.5s initrd +
100 # 10s userspace), and for netboot it travels with the store image.
101 #
102 # It was 29 MB compressed / 83 MB unpacked, of which only 1.8 MB was kernel
103 # modules — the rest was the systemd-initrd closure (openssl, lvm2,
104 # cryptsetup, tpm2-tss, curl, krb5, bash-interactive). Stage 1 only has to
105 # find the boot medium and mount squashfs+overlay: no LUKS, no LVM, no TPM,
106 # no network. The scripted initrd drops that closure: 29 MB -> 11 MB.
107 boot.initrd.systemd.enable = lib.mkForce false;
108 boot.initrd.systemd.emergencyAccess = lib.mkForce false;
109 boot.initrd.services.lvm.enable = lib.mkForce false;
110
111 # Name the onboard Intel NIC deterministically.
112 services.udev.extraRules = ''
113 SUBSYSTEM=="net", ATTR{address}=="70:85:c2:0e:ea:3c", NAME="et0"
114 '';
115
116 # Stateless box: no clan secrets are deployed, so don't try to load an
117 # openssh host key from a vars path that will never exist on the live
118 # system. Let sshd generate an ephemeral key at boot instead.
119 services.openssh.hostKeys = lib.mkForce [
120 {
121 path = "/etc/ssh/ssh_host_ed25519_key";
122 type = "ed25519";
123 }
124 ];
125}