This is a preseed file for Debian 12 (“Bookworm”), set up for a minimal server installation that can be used as a basis to build on.

This is a work in progress, and is subject to updates.

Please don’t rely on this for anything important without reviewing it to make sure it matches your needs.

The easiest way to use this preseed file is if you have a HTTP (not HTTPS) server reachable from the system on which you are installing Debian, because that allows you to use official media to start the installation process. Although not a hard requirement, the process becomes easier if you make the file available at the default path /d-i/bookworm/preseed.cfg. Note that in any case, the file must be served as plain text (not embedded in a web page) and must start with the #_preseed_V1 preamble.

The root password after installation is trivial.

The comments in the file refer to each relevant section in the Debian Bookworm installation guide.

Known issues

  • For some reason, /etc/apt/sources.list does not get populated as intended during the installation process. This is worked around in the late_command which is executed as the installation finishes.
  • The system hostname after installation is the generic debian.


  • The root password is static and in plain text within the preseed configuration. Storing a password hash in the preseed file I publish here would not meaningfully increase security, since I would need to state the root password anyway. (It does however get hashed normally before being stored in the newly installed system’s /etc/shadow.) Change the root password immediately after installation and/or replace the one in the file with one you generate yourself.
  • Creation of an unprivileged account during the installation process is disabled. This is deliberate; create such accounts on an as-needed basis after the installation finishes, ideally through whatever configuration management system you are using.
  • The installed system does not use LVM or LUKS. This is deliberate; this is intended to be for effectively a throwaway installation with nothing on it that is important to keep (a “cattle”, not pet, installation). You can provision the disk with space to spare and/or add more disk or network storage later to mount at relevant mointpoints such as /home, /srv or /var/mail.
  • It would be cleaner to fetch configuration files over the network or from separate media rather than rewriting them based on embedded data in the late_command. However, the current approach makes the preseed configuration self-contained.

Creating a KVM virtual machine

To create a virtual machine and use this to install Debian 12, you can use something similar to:

$ virt-install --connect qemu:///system --memory memory=1024 --sysinfo emulate --vcpus 1 --cpu host --clock offset=utc --boot hd --network network=default,model=virtio --graphics spice --autoconsole graphical --video qxl --cdrom /var/lib/libvirt/images/debian-12.0.0-amd64-DVD-1.iso --name debian12-$(date +%Y%m%d-%H%M%S)-$$-$(printf '%04x' $RANDOM) --disk pool=default,size=5,bus=virtio,format=raw --osinfo name=debian12

The most likely initial points of customization of the above is the values to --connect (virtualization instance to use), --cdrom (path to the installation DVD ISO image), --name (the name of the VM to be created), and --disk (particularly pool, size and format). Note that for clarity, this explicitly specifies some values which are currently defaults. Depending on how recent your host system is, you may need to use something like --osinfo name=debian11 or --osinfo name=linux2022 instead.

The installer will complain about low memory if given less than 1024 MiB RAM, and seems to fail to detect the virtualized network hardware if given less than 480 MiB. The RAM allocation can be reduced after installation; I was able to successfully boot the installed system down to 246 MiB RAM, but not when reducing the RAM allocation further, so 256 MiB is probably a reasonable minimum allocation after installation. 256 MiB also happens to be the listed minimum RAM requirement for Debian 12, so pressing below that, you’re on your own.

The disk needs to be at least 3 GiB for successful installation, although after the installation it only holds about 1 GiB of files (plus a swap partition). If you use something other than bus=virtio, you may need to adjust /dev/vda in the preseed file accordingly. The preseed file sets up a single / file system (including /boot, /home and /usr), plus swap; you can add more disk normally later. To allow some extra space during upgrades and such, I suggest allocating at least 5 GiB for any VMs that you intend to keep around for any real length of time.

Starting the installation

To start the installation:

  1. Boot from the installation media
  2. Once at the GRUB menu and before the countdown timer expires, press Escape
  3. At the boot: prompt, type auto url=fqdn.example and press Enter

You can also provide a full URL such as http://fqdn.example/bookworm-preseed.cfg. Note that at this point in the boot process, the keyboard layout is very likely US English.

If you select the “Automated” installation option in the menu, the installer will prompt for the preseed file’s location.

Once you get past the boot: prompt and especially once you see the progress dialogs flashing past, the process should be 100% hands-off and you can disconnect that viewer session. The VM will power off once installation is complete; simply power it back on to start using the VM. (virt-install will automatically power the VM back on when it powers off if the viewer session is still active.)

Upon boot, for convenience, the system’s IPv4 and IPv6 addresses will be printed on the console at the login prompt. If you don’t want that, delete /etc/issue.d/ip-addresses.issue.

After the installation finishes, password-based login for root is enabled for SSH. To revert to the normal behavior, after you have either set up some other means of authentication for root logins or set up an unprivileged account with sudo access, delete /etc/ssh/sshd_config.d/permit-root-login.conf and run systemctl restart sshd.service.


The most likely points of customization is everything under B.4.1 Localization; under B.4.4 Mirror settings d-i mirror/http/hostname; and under B.5.1 Running custom commands the /etc/apt/sources.list URLs. Also under B.4.5 Account setup you may want to customize the root password (or just log in and change it immediately after installation).


# B.4.1. Localization

d-i debian-installer/language string en
d-i debian-installer/country string SE
d-i debian-installer/locale string en_US.UTF-8
d-i keyboard-configuration/xkb-keymap select se

# B.4.2. Network configuration

# B.4.3. Network console

# B.4.4. Mirror settings

d-i mirror/protocol string http
d-i mirror/country string manual
d-i mirror/http/hostname string
d-i mirror/http/directory string /debian
d-i mirror/http/proxy string
d-i mirror/suite string bookworm

# B.4.5. Account setup

d-i passwd/root-login boolean true
d-i passwd/make-user boolean false
d-i passwd/root-password password trivial
d-i passwd/root-password-again password trivial

# B.4.6. Clock and time zone setup

d-i clock-setup/utc boolean true
d-i time/zone string Etc/UTC
d-i clock-setup/ntp boolean true
d-i clock-setup/ntp-server string

# B.4.7. Partitioning

d-i partman-auto/disk string /dev/vda
d-i partman-auto/method string regular
d-i partman-auto/choose_recipe select atomic
d-i partman-partitioning/confirm_write_new_label boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true

# B.4.7.3. Controlling how partitions are mounted

d-i partman/mount_style select uuid

# B.4.8. Base system installation

d-i base-installer/kernel/image string linux-image-amd64

# B.4.9. Apt setup

d-i apt-setup/non-free-firmware boolean false
d-i apt-setup/non-free boolean false
d-i apt-setup/contrib boolean false
d-i apt-setup/disable-cdrom-entries boolean true
d-i apt-setup/services-select multiselect security, updates
d-i apt-setup/security_host string
d-i debian-installer/allow_unauthenticated boolean false

# B.4.10. Package selection

tasksel tasksel/first multiselect standard, ssh-server
d-i pkgsel/upgrade select full-upgrade
popularity-contest popularity-contest/participate boolean false

# B.4.11. Boot loader installation

d-i grub-installer/only_debian boolean true
d-i grub-installer/bootdev string /dev/vda

# B.4.12. Finishing up the installation

d-i finish-install/reboot_in_progress note
d-i cdrom-detect/eject boolean true

# B.4.13. Preseeding other packages

# B.5.1. Running custom commands during the installation

d-i preseed/late_command string \
  in-target apt-get -y purge installation-report; \
  >>/target/etc/apt/sources.list echo deb bookworm main; \
  >>/target/etc/apt/sources.list echo deb bookworm-security main; \
  >>/target/etc/apt/sources.list echo deb bookworm-updates main; \
  in-target apt-get update; \
  in-target apt-get -y dist-upgrade; \
  mkdir -p /target/etc/issue.d; \
  >>/target/etc/issue.d/ip-addresses.issue echo \\4 \\6; \
  >>/target/etc/issue.d/ip-addresses.issue echo; \
  >/target/etc/nftables.conf echo \#!/usr/sbin/nft -f; \
  >>/target/etc/nftables.conf echo flush ruleset; \
  >>/target/etc/nftables.conf echo table inet filter {; \
  >>/target/etc/nftables.conf echo  chain input {; \
  >>/target/etc/nftables.conf echo   type filter hook input priority filter; \
  >>/target/etc/nftables.conf echo   policy drop; \
  >>/target/etc/nftables.conf echo   ct state established,related accept; \
  >>/target/etc/nftables.conf echo   iifname lo accept; \
  >>/target/etc/nftables.conf echo   ip protocol icmp accept; \
  >>/target/etc/nftables.conf echo   ip6 nexthdr icmpv6 accept; \
  >>/target/etc/nftables.conf echo   ip6 nexthdr ipv6-icmp accept; \
  >>/target/etc/nftables.conf echo   tcp dport 22 accept; \
  >>/target/etc/nftables.conf echo  }; \
  >>/target/etc/nftables.conf echo  chain forward {; \
  >>/target/etc/nftables.conf echo   type filter hook forward priority filter; \
  >>/target/etc/nftables.conf echo   policy accept; \
  >>/target/etc/nftables.conf echo  }; \
  >>/target/etc/nftables.conf echo  chain output {; \
  >>/target/etc/nftables.conf echo   type filter hook output priority filter; \
  >>/target/etc/nftables.conf echo   policy accept; \
  >>/target/etc/nftables.conf echo  }; \
  >>/target/etc/nftables.conf echo }; \
  in-target chmod 700 /etc/nftables.conf; \
  in-target chown root:root /etc/nftables.conf; \
  in-target systemctl enable nftables.service; \
  mkdir -p /target/etc/ssh/sshd_config.d; \
  >>/target/etc/ssh/sshd_config.d/permit-root-login.conf echo PermitRootLogin yes;