1# This module creates a bootable ISO image containing the given NixOS
2# configuration. The derivation for the ISO image will be placed in
3# config.system.build.isoImage.
4
5{ config, lib, pkgs, ... }:
6
7with lib;
8
9let
10 /**
11 * Given a list of `options`, concats the result of mapping each options
12 * to a menuentry for use in grub.
13 *
14 * * defaults: {name, image, params, initrd}
15 * * options: [ option... ]
16 * * option: {name, params, class}
17 */
18 menuBuilderGrub2 =
19 defaults: options: lib.concatStrings
20 (
21 map
22 (option: ''
23 menuentry '${defaults.name} ${
24 # Name appended to menuentry defaults to params if no specific name given.
25 option.name or (if option ? params then "(${option.params})" else "")
26 }' ${if option ? class then " --class ${option.class}" else ""} {
27 linux ${defaults.image} ${defaults.params} ${
28 option.params or ""
29 }
30 initrd ${defaults.initrd}
31 }
32 '')
33 options
34 )
35 ;
36
37 /**
38 * Given a `config`, builds the default options.
39 */
40 buildMenuGrub2 = config:
41 buildMenuAdditionalParamsGrub2 config ""
42 ;
43
44 /**
45 * Given a `config` and params to add to `params`, build a set of default options.
46 * Use this one when creating a variant (e.g. hidpi)
47 */
48 buildMenuAdditionalParamsGrub2 = config: additional:
49 let
50 finalCfg = {
51 name = "NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel}";
52 params = "init=${config.system.build.toplevel}/init ${additional} ${toString config.boot.kernelParams}";
53 image = "/boot/bzImage";
54 initrd = "/boot/initrd";
55 };
56 in
57 menuBuilderGrub2
58 finalCfg
59 [
60 { class = "installer"; }
61 { class = "nomodeset"; params = "nomodeset"; }
62 { class = "copytoram"; params = "copytoram"; }
63 { class = "debug"; params = "debug"; }
64 ]
65 ;
66
67 # Timeout in syslinux is in units of 1/10 of a second.
68 # 0 is used to disable timeouts.
69 syslinuxTimeout = if config.boot.loader.timeout == null then
70 0
71 else
72 max (config.boot.loader.timeout * 10) 1;
73
74
75 max = x: y: if x > y then x else y;
76
77 # The configuration file for syslinux.
78
79 # Notes on syslinux configuration and UNetbootin compatiblity:
80 # * Do not use '/syslinux/syslinux.cfg' as the path for this
81 # configuration. UNetbootin will not parse the file and use it as-is.
82 # This results in a broken configuration if the partition label does
83 # not match the specified config.isoImage.volumeID. For this reason
84 # we're using '/isolinux/isolinux.cfg'.
85 # * Use APPEND instead of adding command-line arguments directly after
86 # the LINUX entries.
87 # * COM32 entries (chainload, reboot, poweroff) are not recognized. They
88 # result in incorrect boot entries.
89
90 baseIsolinuxCfg = ''
91 SERIAL 0 38400
92 TIMEOUT ${builtins.toString syslinuxTimeout}
93 UI vesamenu.c32
94 MENU TITLE NixOS
95 MENU BACKGROUND /isolinux/background.png
96 MENU RESOLUTION 800 600
97 MENU CLEAR
98 MENU ROWS 6
99 MENU CMDLINEROW -4
100 MENU TIMEOUTROW -3
101 MENU TABMSGROW -2
102 MENU HELPMSGROW -1
103 MENU HELPMSGENDROW -1
104 MENU MARGIN 0
105
106 # FG:AARRGGBB BG:AARRGGBB shadow
107 MENU COLOR BORDER 30;44 #00000000 #00000000 none
108 MENU COLOR SCREEN 37;40 #FF000000 #00E2E8FF none
109 MENU COLOR TABMSG 31;40 #80000000 #00000000 none
110 MENU COLOR TIMEOUT 1;37;40 #FF000000 #00000000 none
111 MENU COLOR TIMEOUT_MSG 37;40 #FF000000 #00000000 none
112 MENU COLOR CMDMARK 1;36;40 #FF000000 #00000000 none
113 MENU COLOR CMDLINE 37;40 #FF000000 #00000000 none
114 MENU COLOR TITLE 1;36;44 #00000000 #00000000 none
115 MENU COLOR UNSEL 37;44 #FF000000 #00000000 none
116 MENU COLOR SEL 7;37;40 #FFFFFFFF #FF5277C3 std
117
118 DEFAULT boot
119
120 LABEL boot
121 MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel}
122 LINUX /boot/${config.system.boot.loader.kernelFile}
123 APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
124 INITRD /boot/${config.system.boot.loader.initrdFile}
125
126 # A variant to boot with 'nomodeset'
127 LABEL boot-nomodeset
128 MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (nomodeset)
129 LINUX /boot/${config.system.boot.loader.kernelFile}
130 APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} nomodeset
131 INITRD /boot/${config.system.boot.loader.initrdFile}
132
133 # A variant to boot with 'copytoram'
134 LABEL boot-copytoram
135 MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (copytoram)
136 LINUX /boot/${config.system.boot.loader.kernelFile}
137 APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} copytoram
138 INITRD /boot/${config.system.boot.loader.initrdFile}
139
140 # A variant to boot with verbose logging to the console
141 LABEL boot-debug
142 MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (debug)
143 LINUX /boot/${config.system.boot.loader.kernelFile}
144 APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} loglevel=7
145 INITRD /boot/${config.system.boot.loader.initrdFile}
146 '';
147
148 isolinuxMemtest86Entry = ''
149 LABEL memtest
150 MENU LABEL Memtest86+
151 LINUX /boot/memtest.bin
152 APPEND ${toString config.boot.loader.grub.memtest86.params}
153 '';
154
155 isolinuxCfg = concatStringsSep "\n"
156 ([ baseIsolinuxCfg ] ++ optional config.boot.loader.grub.memtest86.enable isolinuxMemtest86Entry);
157
158 # Setup instructions for rEFInd.
159 refind =
160 if targetArch == "x64" then
161 ''
162 # Adds rEFInd to the ISO.
163 cp -v ${pkgs.refind}/share/refind/refind_x64.efi $out/EFI/boot/
164 ''
165 else
166 "# No refind for ia32"
167 ;
168
169 grubMenuCfg = ''
170 #
171 # Menu configuration
172 #
173
174 insmod gfxterm
175 insmod png
176 set gfxpayload=keep
177
178 # Fonts can be loaded?
179 # (This font is assumed to always be provided as a fallback by NixOS)
180 if loadfont (hd0)/EFI/boot/unicode.pf2; then
181 # Use graphical term, it can be either with background image or a theme.
182 # input is "console", while output is "gfxterm".
183 # This enables "serial" input and output only when possible.
184 # Otherwise the failure mode is to not even enable gfxterm.
185 if test "\$with_serial" == "yes"; then
186 terminal_output gfxterm serial
187 terminal_input console serial
188 else
189 terminal_output gfxterm
190 terminal_input console
191 fi
192 else
193 # Sets colors for the non-graphical term.
194 set menu_color_normal=cyan/blue
195 set menu_color_highlight=white/blue
196 fi
197
198 ${ # When there is a theme configured, use it, otherwise use the background image.
199 if (!isNull config.isoImage.grubTheme) then ''
200 # Sets theme.
201 set theme=(hd0)/EFI/boot/grub-theme/theme.txt
202 # Load theme fonts
203 $(find ${config.isoImage.grubTheme} -iname '*.pf2' -printf "loadfont (hd0)/EFI/boot/grub-theme/%P\n")
204 '' else ''
205 if background_image (hd0)/EFI/boot/efi-background.png; then
206 # Black background means transparent background when there
207 # is a background image set... This seems undocumented :(
208 set color_normal=black/black
209 set color_highlight=white/blue
210 else
211 # Falls back again to proper colors.
212 set menu_color_normal=cyan/blue
213 set menu_color_highlight=white/blue
214 fi
215 ''}
216 '';
217
218 # The EFI boot image.
219 # Notes about grub:
220 # * Yes, the grubMenuCfg has to be repeated in all submenus. Otherwise you
221 # will get white-on-black console-like text on sub-menus. *sigh*
222 efiDir = pkgs.runCommand "efi-directory" {} ''
223 mkdir -p $out/EFI/boot/
224
225 MODULES="fat iso9660 part_gpt part_msdos \
226 normal boot linux configfile loopback chain halt \
227 efifwsetup efi_gop efi_uga \
228 ls search search_label search_fs_uuid search_fs_file \
229 gfxmenu gfxterm gfxterm_background gfxterm_menu test all_video loadenv \
230 exfat ext2 ntfs btrfs hfsplus udf \
231 videoinfo png \
232 echo serial \
233 "
234 # Make our own efi program, we can't rely on "grub-install" since it seems to
235 # probe for devices, even with --skip-fs-probe.
236 ${pkgs.grub2_efi}/bin/grub-mkimage -o $out/EFI/boot/${if targetArch == "x64" then "bootx64" else "bootx32"}.efi -p /EFI/boot -O ${if targetArch == "x64" then "x86_64" else "i386"}-efi \
237 $MODULES
238 cp ${pkgs.grub2_efi}/share/grub/unicode.pf2 $out/EFI/boot/
239
240 cat <<EOF > $out/EFI/boot/grub.cfg
241
242 # If you want to use serial for "terminal_*" commands, you need to set one up:
243 # Example manual configuration:
244 # → serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1
245 # This uses the defaults, and makes the serial terminal available.
246 set with_serial=no
247 if serial; then set with_serial=yes ;fi
248 export with_serial
249 clear
250 set timeout=10
251 ${grubMenuCfg}
252
253 #
254 # Menu entries
255 #
256
257 ${buildMenuGrub2 config}
258 submenu "HiDPI, Quirks and Accessibility" --class hidpi --class submenu {
259 ${grubMenuCfg}
260 submenu "Suggests resolution @720p" --class hidpi-720p {
261 ${grubMenuCfg}
262 ${buildMenuAdditionalParamsGrub2 config "video=1280x720@60"}
263 }
264 submenu "Suggests resolution @1080p" --class hidpi-1080p {
265 ${grubMenuCfg}
266 ${buildMenuAdditionalParamsGrub2 config "video=1920x1080@60"}
267 }
268
269 # Some laptop and convertibles have the panel installed in an
270 # inconvenient way, rotated away from the keyboard.
271 # Those entries makes it easier to use the installer.
272 submenu "" {return}
273 submenu "Rotate framebuffer Clockwise" --class rotate-90cw {
274 ${grubMenuCfg}
275 ${buildMenuAdditionalParamsGrub2 config "fbcon=rotate:1"}
276 }
277 submenu "Rotate framebuffer Upside-Down" --class rotate-180 {
278 ${grubMenuCfg}
279 ${buildMenuAdditionalParamsGrub2 config "fbcon=rotate:2"}
280 }
281 submenu "Rotate framebuffer Counter-Clockwise" --class rotate-90ccw {
282 ${grubMenuCfg}
283 ${buildMenuAdditionalParamsGrub2 config "fbcon=rotate:3"}
284 }
285
286 # As a proof of concept, mainly. (Not sure it has accessibility merits.)
287 submenu "" {return}
288 submenu "Use black on white" --class accessibility-blakconwhite {
289 ${grubMenuCfg}
290 ${buildMenuAdditionalParamsGrub2 config "vt.default_red=0xFF,0xBC,0x4F,0xB4,0x56,0xBC,0x4F,0x00,0xA1,0xCF,0x84,0xCA,0x8D,0xB4,0x84,0x68 vt.default_grn=0xFF,0x55,0xBA,0xBA,0x4D,0x4D,0xB3,0x00,0xA0,0x8F,0xB3,0xCA,0x88,0x93,0xA4,0x68 vt.default_blu=0xFF,0x58,0x5F,0x58,0xC5,0xBD,0xC5,0x00,0xA8,0xBB,0xAB,0x97,0xBD,0xC7,0xC5,0x68"}
291 }
292
293 # Serial access is a must!
294 submenu "" {return}
295 submenu "Serial console=ttyS0,115200n8" --class serial {
296 ${grubMenuCfg}
297 ${buildMenuAdditionalParamsGrub2 config "console=ttyS0,115200n8"}
298 }
299 }
300
301 menuentry 'rEFInd' --class refind {
302 # UUID is hard-coded in the derivation.
303 search --set=root --no-floppy --fs-uuid 1234-5678
304 chainloader (\$root)/EFI/boot/refind_x64.efi
305 }
306 menuentry 'Firmware Setup' --class settings {
307 fwsetup
308 clear
309 echo ""
310 echo "If you see this message, your EFI system doesn't support this feature."
311 echo ""
312 }
313 menuentry 'Shutdown' --class shutdown {
314 halt
315 }
316 EOF
317
318 ${refind}
319 '';
320
321 efiImg = pkgs.runCommand "efi-image_eltorito" { buildInputs = [ pkgs.mtools pkgs.libfaketime ]; }
322 # Be careful about determinism: du --apparent-size,
323 # dates (cp -p, touch, mcopy -m, faketime for label), IDs (mkfs.vfat -i)
324 ''
325 mkdir ./contents && cd ./contents
326 cp -rp "${efiDir}"/* .
327 mkdir ./boot
328 cp -p "${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}" \
329 "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}" ./boot/
330 touch --date=@0 ./*
331
332 usage_size=$(du -sb --apparent-size . | tr -cd '[:digit:]')
333 # Make the image 110% as big as the files need to make up for FAT overhead
334 image_size=$(( ($usage_size * 110) / 100 ))
335 # Make the image fit blocks of 1M
336 block_size=$((1024*1024))
337 image_size=$(( ($image_size / $block_size + 1) * $block_size ))
338 echo "Usage size: $usage_size"
339 echo "Image size: $image_size"
340 truncate --size=$image_size "$out"
341 ${pkgs.libfaketime}/bin/faketime "2000-01-01 00:00:00" ${pkgs.dosfstools}/sbin/mkfs.vfat -i 12345678 -n EFIBOOT "$out"
342 mcopy -bpsvm -i "$out" ./* ::
343 ''; # */
344
345 targetArch = if pkgs.stdenv.isi686 then
346 "ia32"
347 else if pkgs.stdenv.isx86_64 then
348 "x64"
349 else
350 throw "Unsupported architecture";
351
352in
353
354{
355 options = {
356
357 isoImage.isoName = mkOption {
358 default = "${config.isoImage.isoBaseName}.iso";
359 description = ''
360 Name of the generated ISO image file.
361 '';
362 };
363
364 isoImage.isoBaseName = mkOption {
365 default = "nixos";
366 description = ''
367 Prefix of the name of the generated ISO image file.
368 '';
369 };
370
371 isoImage.compressImage = mkOption {
372 default = false;
373 description = ''
374 Whether the ISO image should be compressed using
375 <command>bzip2</command>.
376 '';
377 };
378
379 isoImage.volumeID = mkOption {
380 default = "NIXOS_BOOT_CD";
381 description = ''
382 Specifies the label or volume ID of the generated ISO image.
383 Note that the label is used by stage 1 of the boot process to
384 mount the CD, so it should be reasonably distinctive.
385 '';
386 };
387
388 isoImage.contents = mkOption {
389 example = literalExample ''
390 [ { source = pkgs.memtest86 + "/memtest.bin";
391 target = "boot/memtest.bin";
392 }
393 ]
394 '';
395 description = ''
396 This option lists files to be copied to fixed locations in the
397 generated ISO image.
398 '';
399 };
400
401 isoImage.storeContents = mkOption {
402 example = literalExample "[ pkgs.stdenv ]";
403 description = ''
404 This option lists additional derivations to be included in the
405 Nix store in the generated ISO image.
406 '';
407 };
408
409 isoImage.includeSystemBuildDependencies = mkOption {
410 default = false;
411 description = ''
412 Set this option to include all the needed sources etc in the
413 image. It significantly increases image size. Use that when
414 you want to be able to keep all the sources needed to build your
415 system or when you are going to install the system on a computer
416 with slow or non-existent network connection.
417 '';
418 };
419
420 isoImage.makeEfiBootable = mkOption {
421 default = false;
422 description = ''
423 Whether the ISO image should be an efi-bootable volume.
424 '';
425 };
426
427 isoImage.makeUsbBootable = mkOption {
428 default = false;
429 description = ''
430 Whether the ISO image should be bootable from CD as well as USB.
431 '';
432 };
433
434 isoImage.efiSplashImage = mkOption {
435 default = pkgs.fetchurl {
436 url = https://raw.githubusercontent.com/NixOS/nixos-artwork/a9e05d7deb38a8e005a2b52575a3f59a63a4dba0/bootloader/efi-background.png;
437 sha256 = "18lfwmp8yq923322nlb9gxrh5qikj1wsk6g5qvdh31c4h5b1538x";
438 };
439 description = ''
440 The splash image to use in the EFI bootloader.
441 '';
442 };
443
444 isoImage.splashImage = mkOption {
445 default = pkgs.fetchurl {
446 url = https://raw.githubusercontent.com/NixOS/nixos-artwork/a9e05d7deb38a8e005a2b52575a3f59a63a4dba0/bootloader/isolinux/bios-boot.png;
447 sha256 = "1wp822zrhbg4fgfbwkr7cbkr4labx477209agzc0hr6k62fr6rxd";
448 };
449 description = ''
450 The splash image to use in the legacy-boot bootloader.
451 '';
452 };
453
454 isoImage.grubTheme = mkOption {
455 default = pkgs.nixos-grub2-theme;
456 type = types.nullOr (types.either types.path types.package);
457 description = ''
458 The grub2 theme used for UEFI boot.
459 '';
460 };
461
462 isoImage.appendToMenuLabel = mkOption {
463 default = " Installer";
464 example = " Live System";
465 description = ''
466 The string to append after the menu label for the NixOS system.
467 This will be directly appended (without whitespace) to the NixOS version
468 string, like for example if it is set to <literal>XXX</literal>:
469
470 <para><literal>NixOS 99.99-pre666XXX</literal></para>
471 '';
472 };
473
474 };
475
476 config = {
477
478 boot.loader.grub.version = 2;
479
480 # Don't build the GRUB menu builder script, since we don't need it
481 # here and it causes a cyclic dependency.
482 boot.loader.grub.enable = false;
483
484 # !!! Hack - attributes expected by other modules.
485 system.boot.loader.kernelFile = "bzImage";
486 environment.systemPackages = [ pkgs.grub2 pkgs.grub2_efi pkgs.syslinux ];
487
488 # In stage 1 of the boot, mount the CD as the root FS by label so
489 # that we don't need to know its device. We pass the label of the
490 # root filesystem on the kernel command line, rather than in
491 # `fileSystems' below. This allows CD-to-USB converters such as
492 # UNetbootin to rewrite the kernel command line to pass the label or
493 # UUID of the USB stick. It would be nicer to write
494 # `root=/dev/disk/by-label/...' here, but UNetbootin doesn't
495 # recognise that.
496 boot.kernelParams =
497 [ "root=LABEL=${config.isoImage.volumeID}"
498 "boot.shell_on_fail"
499 ];
500
501 fileSystems."/" =
502 { fsType = "tmpfs";
503 options = [ "mode=0755" ];
504 };
505
506 # Note that /dev/root is a symlink to the actual root device
507 # specified on the kernel command line, created in the stage 1
508 # init script.
509 fileSystems."/iso" =
510 { device = "/dev/root";
511 neededForBoot = true;
512 noCheck = true;
513 };
514
515 # In stage 1, mount a tmpfs on top of /nix/store (the squashfs
516 # image) to make this a live CD.
517 fileSystems."/nix/.ro-store" =
518 { fsType = "squashfs";
519 device = "/iso/nix-store.squashfs";
520 options = [ "loop" ];
521 neededForBoot = true;
522 };
523
524 fileSystems."/nix/.rw-store" =
525 { fsType = "tmpfs";
526 options = [ "mode=0755" ];
527 neededForBoot = true;
528 };
529
530 fileSystems."/nix/store" =
531 { fsType = "unionfs-fuse";
532 device = "unionfs";
533 options = [ "allow_other" "cow" "nonempty" "chroot=/mnt-root" "max_files=32768" "hide_meta_files" "dirs=/nix/.rw-store=rw:/nix/.ro-store=ro" ];
534 };
535
536 boot.initrd.availableKernelModules = [ "squashfs" "iso9660" "uas" ];
537
538 boot.blacklistedKernelModules = [ "nouveau" ];
539
540 boot.initrd.kernelModules = [ "loop" ];
541
542 # Closures to be copied to the Nix store on the CD, namely the init
543 # script and the top-level system configuration directory.
544 isoImage.storeContents =
545 [ config.system.build.toplevel ] ++
546 optional config.isoImage.includeSystemBuildDependencies
547 config.system.build.toplevel.drvPath;
548
549 # Create the squashfs image that contains the Nix store.
550 system.build.squashfsStore = pkgs.callPackage ../../../lib/make-squashfs.nix {
551 storeContents = config.isoImage.storeContents;
552 };
553
554 # Individual files to be included on the CD, outside of the Nix
555 # store on the CD.
556 isoImage.contents =
557 [ { source = pkgs.substituteAll {
558 name = "isolinux.cfg";
559 src = pkgs.writeText "isolinux.cfg-in" isolinuxCfg;
560 bootRoot = "/boot";
561 };
562 target = "/isolinux/isolinux.cfg";
563 }
564 { source = config.boot.kernelPackages.kernel + "/" + config.system.boot.loader.kernelFile;
565 target = "/boot/" + config.system.boot.loader.kernelFile;
566 }
567 { source = config.system.build.initialRamdisk + "/" + config.system.boot.loader.initrdFile;
568 target = "/boot/" + config.system.boot.loader.initrdFile;
569 }
570 { source = config.system.build.squashfsStore;
571 target = "/nix-store.squashfs";
572 }
573 { source = "${pkgs.syslinux}/share/syslinux";
574 target = "/isolinux";
575 }
576 { source = config.isoImage.efiSplashImage;
577 target = "/EFI/boot/efi-background.png";
578 }
579 { source = config.isoImage.splashImage;
580 target = "/isolinux/background.png";
581 }
582 { source = pkgs.writeText "version" config.system.nixos.label;
583 target = "/version.txt";
584 }
585 ] ++ optionals config.isoImage.makeEfiBootable [
586 { source = efiImg;
587 target = "/boot/efi.img";
588 }
589 { source = "${efiDir}/EFI";
590 target = "/EFI";
591 }
592 ] ++ optionals config.boot.loader.grub.memtest86.enable [
593 { source = "${pkgs.memtest86plus}/memtest.bin";
594 target = "/boot/memtest.bin";
595 }
596 ] ++ optionals (!isNull config.isoImage.grubTheme) [
597 { source = config.isoImage.grubTheme;
598 target = "/EFI/boot/grub-theme";
599 }
600 ];
601
602 boot.loader.timeout = 10;
603
604 # Create the ISO image.
605 system.build.isoImage = pkgs.callPackage ../../../lib/make-iso9660-image.nix ({
606 inherit (config.isoImage) isoName compressImage volumeID contents;
607 bootable = true;
608 bootImage = "/isolinux/isolinux.bin";
609 } // optionalAttrs config.isoImage.makeUsbBootable {
610 usbBootable = true;
611 isohybridMbrImage = "${pkgs.syslinux}/share/syslinux/isohdpfx.bin";
612 } // optionalAttrs config.isoImage.makeEfiBootable {
613 efiBootable = true;
614 efiBootImage = "boot/efi.img";
615 });
616
617 boot.postBootCommands =
618 ''
619 # After booting, register the contents of the Nix store on the
620 # CD in the Nix database in the tmpfs.
621 ${config.nix.package.out}/bin/nix-store --load-db < /nix/store/nix-path-registration
622
623 # nixos-rebuild also requires a "system" profile and an
624 # /etc/NIXOS tag.
625 touch /etc/NIXOS
626 ${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
627 '';
628
629 # Add vfat support to the initrd to enable people to copy the
630 # contents of the CD to a bootable USB stick.
631 boot.initrd.supportedFilesystems = [ "vfat" ];
632
633 };
634
635}