Netboot
Appearance
Building and serving a netboot image
This provides an easy way to serve the NixOS installer over netboot, such as when you already have a working NixOS machine and want to install NixOS on a second machine connected to the same network.
Example
This example uses Pixiecore for hosting, which works in an ordinary network environment with an existing DHCP server.
Pixiecore will notice when the booted machine talks to the network's existing DHCP server, and send netboot information to it at that time.
Create file system.nix
:
let
# NixOS 22.11 as of 2023-01-12
nixpkgs = builtins.getFlake "github:nixos/nixpkgs/54644f409ab471e87014bb305eac8c50190bcf48";
sys = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
({ config, pkgs, lib, modulesPath, ... }: {
imports = [
(modulesPath + "/installer/netboot/netboot-minimal.nix")
];
config = {
## Some useful options for setting up a new system
# services.getty.autologinUser = lib.mkForce "root";
# users.users.root.openssh.authorizedKeys.keys = [ ... ];
# console.keyMap = "de";
# hardware.video.hidpi.enable = true;
system.stateVersion = config.system.nixos.release;
};
})
];
};
run-pixiecore = let
hostPkgs = if sys.pkgs.system == builtins.currentSystem
then sys.pkgs
else nixpkgs.legacyPackages.${builtins.currentSystem};
build = sys.config.system.build;
in hostPkgs.writers.writeBash "run-pixiecore" ''
exec ${hostPkgs.pixiecore}/bin/pixiecore \
boot ${build.kernel}/bzImage ${build.netbootRamdisk}/initrd \
--cmdline "init=${build.toplevel}/init loglevel=4" \
--debug --dhcp-no-bind \
--port 64172 --status-port 64172 "$@"
'';
in
run-pixiecore
Building:
# Build pixiecore runner
nix-build system.nix -o /tmp/run-pixiecore
Running:
# Open required firewall ports
sudo iptables -w -I nixos-fw -p udp -m multiport --dports 67,69,4011 -j ACCEPT
sudo iptables -w -I nixos-fw -p tcp -m tcp --dport 64172 -j ACCEPT
# Run pixiecore
sudo $(realpath /tmp/run-pixiecore)
# Close ports
sudo iptables -w -D nixos-fw -p udp -m multiport --dports 67,69,4011 -j ACCEPT
sudo iptables -w -D nixos-fw -p tcp -m tcp --dport 64172 -j ACCEPT
Another example
❄︎ netboot.nix
{
name ? "netboot",
arch ? "x86_64-linux",
configuration ? _: { }, # --arg configuration 'import ./netboot-config.nix'
legacy ? false, # variation with pxelinux and dnsmasq for older systems
cmdline ? [ ],
loglevel ? 4,
pixiecoreport ? 64172,
proxynets ? [ "192.168.0.0" ],
serialconsole ? false,
serialport ? 0,
serialspeed ? 9600,
nixpkgs ? import <nixpkgs> { },
...
}:
with nixpkgs;
with lib;
let
example-configuration =
{ pkgs, config, ... }:
with pkgs;
{
config = {
environment.systemPackages = [
mtr
bridge-utils
vlan
ethtool
jwhois
sipcalc
netcat-openbsd
tsocks
psmisc
pciutils
usbutils
lm_sensors
dmidecode
microcom
unar
mkpasswd
ripgrep
wget
rsync
sshfs-fuse
iperf3
mc
mutt
borgbackup
rxvt-unicode
];
# users.users.nixos.openssh.authorizedKeys.keys = [ … ];
# services.openssh = { ports = [2]; settings.PasswordAuthentication = false; };
# virtualisation.lxc.enable = true;
};
};
config = import <nixpkgs/nixos/lib/eval-config.nix> {
# see <nixpkgs/nixos/release.nix>
system = arch;
modules = [
<nixpkgs/nixos/modules/installer/netboot/netboot-minimal.nix>
# Reduce build time by ~7x (~1 minute instead of many minutes) by not using the highest compression (image is 5% larger).
({ ... }: { netboot.squashfsCompression = "zstd -Xcompression-level 6"; })
version-module
example-configuration
configuration
];
};
version-module =
{ config, ... }:
{
system.stateVersion = builtins.substring 0 (builtins.stringLength "XX.XX") config.system.nixos.version;
system.nixos.tags = [ name ];
};
run-pixiecore = writeShellScript "${name}-run-pixiecore" ''
exec ${pixiecore}/bin/pixiecore \
boot ${kernel} ${initrd} \
--cmdline "${cmd-line}" \
--debug --dhcp-no-bind --log-timestamps \
--port ${toString pixiecoreport} \
--status-port ${toString pixiecoreport} "$@"
'';
run-dnsmasq = writeShellScript "${name}-run-dnsmasq" ''
exec ${dnsmasq}/bin/dnsmasq \
-d -k --no-daemon -C "${dnsmasq-conf}" "$@"
'';
tftp-root = linkFarm "${name}-tftp-root" (
mapAttrsToList (name: path: { inherit name path; }) {
"pxelinux.cfg/default" = pxelinux-cfg;
"pxelinux.0" = "syslinux/pxelinux.0";
"syslinux" = "${syslinux}/share/syslinux";
"bzImage" = kernel;
"initrd" = initrd;
}
);
dnsmasq-conf = writeText "${name}-dnsmasq-conf" ''
pxe-prompt="Booting NixOS..",1
local-service=net
dhcp-boot=pxelinux.0
${flip concatMapStrings proxynets (net: ''
dhcp-range=${net},proxy
'')}
dhcp-no-override
dhcp-leasefile=/dev/null
log-dhcp
enable-tftp
tftp-port-range=6900,6999
tftp-root=${tftp-root}
'';
cmd-line = concatStringsSep " " (
[
"init=${build.toplevel}/init"
"loglevel=${toString loglevel}"
]
++ optional serialconsole "console=ttyS${toString serialport},${toString serialspeed}"
++ cmdline
);
pxelinux-cfg = writeText "${name}-pxelinux.cfg" ''
${optionalString serialconsole "serial ${toString serialport} ${toString serialspeed}"}
console 1
prompt 1
timeout 37
default NixOS
label NixOS
kernel bzImage
append initrd=initrd ${cmd-line}
'';
build = config.config.system.build;
kernel = "${build.kernel}/${kernel-target}";
kernel-target = config.pkgs.stdenv.hostPlatform.linux-kernel.target;
initrd = "${build.netbootRamdisk}/initrd";
in
if legacy then run-dnsmasq else run-pixiecore
Building:
# Build pixiecore runner
nix-build netboot.nix -o /tmp/run-pixiecore
# Build dnsmasq + pxelinux runner
nix-build netboot.nix --arg legacy true -o /tmp/run-dnsmasq
# Build for some ancient system with a serial console
nix-build netboot.nix --arg name '"ancient-netboot"' -o /tmp/run-netboot \
--arg configuration 'import ./ancient-config.nix' \
--arg legacy true --arg proxynets '["10.2.1.0"]' \
--arg serialconsole true --arg serialport 3 --arg serialspeed 115200
Running:
- Run the example exactly like the other example further up on the page.
Troubleshooting
- Error "autoexec.ipxe... Operation not supported": See this issue.
See also
NixOS: Pixiecore module.
NixOS manual: PXE booting.
netboot.xyz
There is now official netboot.xyz support. Just select NixOS from Linux installs and you should be ready to go.
Note: Your iPXE must be recent enough to support https:// links