Back in 2020, I used the Debian installer’s pre-seed capability to automatically build KVM-based VMs. In my new lab network I have 10 systems that are headless, attached to managed PDUs (so can be remotely power cycled) but have no integrated console or KVM attached. Debian Installer has a network console feature, which allows remote installs over SSH. This post describes automating the setup of that so a network-booted host in the lab network will, by default, boot into the Debian Installer for the current stable distribution ready for me to remotely SSH in and complete the install.

Crafting the pre-seed file

I already have a number of pre-seed files, including the one I previously created for building KVM VMs, in a Git repository. They are already deployed to a webserver under a debian-preseed virtual host on both the lab and live network, so this pre-seed file is simply added to their number (I called it pressed-network-console.cfg).

The minimally relevant part to enable the network console is this, noting the public part of the ssh-key that will be used to login will need to be placed alongside the pre-seed file (I used .gitignore to ensure it was not added to the git repository):

# Network install console
d-i anna/choose_modules string network-console
d-i network-console/authorized_keys_url string http://debian-preseed/d-i/buster/network-console.key
# Commented out for key-only access
#d-i network-console/password password r00tme
#d-i network-console/password-again password r00tme

I also added the following defaults to it, for localisation, time and mirrors to it, just to make my life easier:

# Localisation
d-i debian-installer/locale string en_GB.UTF-8
d-i keyboard-configuration/xkb-keymap select gb

# Time
d-i time/zone select Europe/London
# HW clock set to UTC?
d-i clock-setup/utc boolean true
# Use NTP during install
d-i clock-setup/ntp boolean true
d-i clock-setup/ntp-server string ntp

# Mirror
d-i mirror/country string manual
d-i mirror/http/hostname string mirror
d-i mirror/http/directory string /mirrors/debian
d-i mirror/http/proxy string

PXE boot options

I order to make the installer auto-boot to the point of having the network-console up, I have to pre-configure some settings via the kernel command line. This meant setting auto-install/enable=true, which delays locale and keyboard configuration until after any pre-seed file has been read and skips questions answered in the pre-seed - it will still prompt for anything enabled at the current question priority that is not set via pre-seed.

I also found that in order to bring up the network, before it got to downloading the pre-seed, I needed to set netcfg/get_hostname and netcfg/get_domain on the kernel command line to stop it prompting for hostname and domain. This is described in the example pre-seed:

Any hostname and domain names assigned from dhcp take precedence over

values set here. However, setting the values still prevents the questions

from being shown, even if values come from dhcp., these are overridden

d-i netcfg/get_hostname string unassigned-hostname d-i netcfg/get_domain string unassigned-domain

On my system, I set hostname to debian (the default if Debian Installer cannot determine a hostname via DHCP or reverse DNS) and the domain to the empty string (again, mirroring default behaviour). In order to ensure that it was always able to determine the names in my home-lab, I added hostname proxmox-1 through to proxmox-10 to DNS and DHCP at this point.

The full kernel command line now looks something like this:

linux initrd=initrd.gz auto-install/enable=true netcfg/get_hostname=debian netcfg/get_domin= preseed/url=http://debian-preseed/d-i/buster/preseed-network-console.cfg -- quiet

In order to be able to install headless boxes easily, I modified by existing iPXE menu that previously had no timeout so waited indefinitely for the user to select a boot option. I changed it to automatically boot the ‘network console’ version (i.e. with the preseed options) of the ‘bullseye’ installer (current stable, at time of writing) after 20 seconds, which I felt was a good balance between long enough to intercept it if required and not too long to wait remotely for the installer to start:

choose --default bullseye-amd64-network-console --timeout 20000 option && goto ${option}


One thing that I had not configured was for the DHCP server to hand out hostnames (the Debian Installer falls back to doing a reverse-lookup on the IP address). The ISC DHCP Server can also be configured to lookup lease hostnames from DNS by setting get-lease-hostnames (or they can be statically configured). The wording on the man page implies to me that this lookup takes place once when the server starts, rather than dynamically as each client requests its IP - which might be useful information if newly added DNS entires are not picked up: “The get-lease-hostnames statement is used to tell dhcpd whether or not to look up the domain name corresponding to the IP address of each address in the lease pool and use that address for the DHCP hostname option. If flag is true, then this lookup is done for all addresses in the current scope.”

get-lease-hostnames true;

I had hoped that setting the hostname by DHCP might have negated the need to set the netfg/get_... options on the kernel command-line, but this was not the case (logs confirmed it had not fallen back to rDNS method to get the hostname, so that was working correctly). I left this change in place, however, as it is appropriate for clients whose names are known. It also makes setting generic hostnames for dynamic clients (e.g. ‘dhcp-100’ for IP x.100) as easy as adding DNS entries (no further changes to the dhcpd.conf file).