Atom Feed
Comments Atom Feed


Similar Articles

12/07/2015 07:55
Home Lab Project: Kickstart

Recent Articles

17/07/2016 15:23
AWS ssh known_host sync
11/07/2016 08:42
File integrity and log anomaly auditing Updated (like fcheck/logcheck)
30/05/2016 13:09
Xenial LXC Container on Debian
12/03/2016 15:33
PHP Zend opcache on Cacti via SNMP
11/03/2016 13:11
Git on (Smart) HTTP with read/write authentication

Glen Pitt-Pladdy :: Blog

Home Lab Project: Debian/Ubuntu Preseed

Following on from building a Red Hat/CentOS Kickstart environment, I've now moved on to building my Debian/Ubuntu Preseed environment.

This uses a lot of very similar principles so I'm going to skip over the common stuff and just refer the prior configuration. The good news is that while it may seem much more daunting to build the Debian Preseed file than the Kickstart which you can base on the /root/anaconda-ks.cfg left after a Red Hat / CentOS install, it's actually taken much longer and more iterations to get the Kickstart to the same level of refinement. In fact, I did Kickstart first, but I'm still iterating on it (and delaying publishing this article) when I'm satisfied this Debian Preseed is complete.

Common Configuration

You will need to get the "PXE (Network) Boot" and "PXELINUX" stuff working as before.

Once you have those working then we can take a look at where it changes...

Boot Images

While Debian provides a lot of docs for this, putting together all parts of the puzzle may take a bit of care. My main reference for this is the Debian PXEBootInstall wiki page and Debian Installation Guide Appendix on Preseeding. There is an equivalent Ubuntu Installation Guide Appendix on Preseeding for those going that route, but I've found a few discrepencies in the Ubuntu Installer.

Like before, we need boot files but this time Debian has conveniently provided this for you neatly packaged:

# apt-get install debian-installer-8-netboot-amd64

Most of what is there we don't need, but the key two files are:

# cp /usr/lib/debian-installer/images/8/amd64/text/debian-installer/amd64/linux /usr/lib/debian-installer/images/8/amd64/text/debian-installer/amd64/initrd.gz /srv/tftp/debian-jessie-amd64/

Another option I use for Ubuntu is to grab the files of an iso:

# cp /iso/mountpoint/install/netboot/ubuntu-installer/amd64/linux /iso/mountpoint/install/netboot/ubuntu-installer/amd64/initrd.gz /srv/tftp/ubuntu-trusty-amd64/

Note again that I've got a separate directory below srv/tftp/ for the Debian Jessie boot files which allow easy management of boot images for the distros I am building. You could also do other tricks like bind mounting or changing the root of TFTP to avoid copying the files, but for me this is most practical.

IMPORTANT: After a while of use you may start to get errors in the partitioning phase of the install that breaks the install, and specifically the log will say stuff about unknown symbols __bread_gfp and __getblk_gfp - this is a very simple but can waste a lot of time. When a new point release comes out the kernel modules that get downloaded automatically might not be compatible with the initial boot images you copied above. The answer is simply to update them by repeating the above with the newer images.


For this our PXELINUX config will be very similar to before, just with Preseed options:

# some comment - we like comments!
default linux
label linux
        kernel debian-jessie-amd64/linux
        append initrd=debian-jessie-amd64/initrd.gz netcfg/choose_interface=eth0 netcfg/get_hostname=debianpxe netcfg/ preseed/url=tftp://ip.of.TFTP.server/preseed/<01-MAC-address-lower-case-hyphen-separated> preseed/url/checksum=<md5checksum> auto-install/enable=true

In this case I'm providing my Preeseed file the same as the PXELINUX config - a subdirectory and matching MAC address based naming.

For Ubuntu (trusty) I add keyboard selection to prevent prompting in the installer:

# some comment - we like comments!
default linux
label linux
        kernel ubuntu-trusty-amd64/linux
        append initrd=ubuntu-trusty-amd64/initrd.gz netcfg/choose_interface=eth0 netcfg/get_hostname=debianpxe netcfg/ preseed/url=tftp://ip.of.TFTP.server/preseed/<01-MAC-address-lower-case-hyphen-separated> preseed/url/checksum=<md5checksum> auto-install/enable=true console-setup/ask_detect=false keyboard-configuration/layoutcode=uk

Also note that the preseed/url/checksum= is optional if you want to validate the integrity of the preseed file, but that also means you will need to update it every time you tweak the preseed file.

The key thing to note is the auto-install/enable=true (or just "auto") which means that it will just get on with it and not prompt when it has a preseeded value.

With Debian Preseed and "auto" install runs on it's own

More boot options are available in Debian Installation Guide.

Preseed File

Like with Kickstart, it's beyond the scope of this article to give a detailed description of how to build a Preseed file. There is further documentation in the Debian Installation Guide on Creating the Preeseed file. If you go through the options carefully and paste them into your file chances are you will get there sooner than you think. If you rush and try and cut corners without paying attention to the detail, then chances are you will cause yourself a lot of pain and take much longer.

For those into automated provisioning (aka Devops) note the hooks possible to execute things at the end of the install. This is the ideal opportunity to deploy ssh keys for Chef bootstrapping, or maybe just a script to complete the build or something similar.


If you want more control over how partitioning is done then this is the most difficult bit by far. In my case I have small VMs and want to configure a minimal LVM setup with some spare space to allocate later. That turns out to be rather tricky.

The best sources of information I found on this are:

... but it still takes a lot of figuring out

One of the most annoying things is that this doesn't seem to understand the concept (at least that's what the docs say) of leaving free space in a VG which is kind of the point - give flexibility and can be used for things like snapshots.

To achieve this you have to create another volume called "deleteme" to take up the free space, and then run lvremove after install. Messy!


An example of a sanitised minimal file for a 1024MiB / 8GiB KVM VM which was put together by copy & paste for a Server with GUI I run on KVM is:

# Preseeding only locale sets language, country and locale.
d-i debian-installer/locale string en_GB

# Keyboard selection.
d-i keyboard-configuration/xkb-keymap select gb

# If you select ftp, the mirror/country string does not need to be set.
#d-i mirror/protocol string ftp
d-i mirror/country string manual
d-i mirror/http/hostname string
#d-i mirror/http/hostname string
d-i mirror/http/directory string /debian
#d-i mirror/http/directory string /ubuntu
d-i mirror/http/proxy string http://address.of.apt.cacher:3142

# For Ubuntu you will need to explicitly enable root if you don't want to create a user
# Skip creation of a root account (normal user account will be able to
# use sudo).
#d-i passwd/root-login boolean false
# Alternatively, to skip creation of a normal user account.
d-i passwd/make-user boolean false

# For Ubuntu you will want to set this to avoid questions
# Set to true if you want to encrypt the first user's home directory.
#d-i user-setup/encrypt-home boolean false

# Root password, either in clear text
#d-i passwd/root-password password r00tme
#d-i passwd/root-password-again password r00tme
# or encrypted using an MD5 hash.
d-i passwd/root-password-crypted password HASHEDPASSWORD
# use mkpasswd -m sha-512

# Controls whether or not the hardware clock is set to UTC.
d-i clock-setup/utc boolean true

# You may set this to any valid setting for $TZ; see the contents of
# /usr/share/zoneinfo/ for valid values.
d-i time/zone string Europe/London

# Controls whether to use NTP to set the clock during the install
d-i clock-setup/ntp boolean true
# NTP server to use. The default is almost always fine here.
d-i clock-setup/ntp-server string address.of.ntp.server
# Alternatively, you may specify a disk to partition. If the system has only

# one disk the installer will default to using that, but otherwise the device
# name must be given in traditional, non-devfs format (so e.g. /dev/sda
# and not e.g. /dev/discs/disc0/disc).
# For example, to use the first SCSI/SATA hard disk:
#d-i partman-auto/disk string /dev/sda
# In addition, you'll need to specify the method to use.
# The presently available methods are:
# - regular: use the usual partition types for your architecture
# - lvm:     use LVM to partition the disk
# - crypto:  use LVM within an encrypted partition
d-i partman-auto/method string lvm

# If one of the disks that are going to be automatically partitioned
# contains an old LVM configuration, the user will normally receive a
# warning. This can be preseeded away...
d-i partman-lvm/device_remove_lvm boolean true
# The same applies to pre-existing software RAID array:
d-i partman-md/device_remove_md boolean true
# And the same goes for the confirmation to write the lvm partitions.
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true

# For Ubuntu you will need to set this (probably max) else it will ask
# For LVM partitioning, you can select how much of the volume group to use # for logical volumes.
#d-i partman-auto-lvm/guided_size string max
#d-i partman-auto-lvm/guided_size string 10GB
#d-i partman-auto-lvm/guided_size string 50%

# If not, you can put an entire recipe into the preconfiguration file in one
# (logical) line.
d-i partman-auto/expert_recipe string                   \
        boot-lvm ::                                     \
        128 1000000 256 ext4                            \
                $primary{ }                             \
                method{ format } format{ }              \
                use_filesystem{ } filesystem{ ext4 }    \
                label { /boot }                         \
                mountpoint{ /boot }                     \
                .                                       \
        4096 1000 1000000 lvm                           \
                $primary{ }                             \
                $defaultignore{ }                       \
                method{ lvm }                           \
                vg_name{ vg00 }                         \
                .                                       \
        2048 4096 4096 ext4                             \
                $lvmok{ }                               \
                in_vg{ vg00 }                           \
                lv_name{ root }                         \
                method{ format } format{ }              \
                label { root }                          \
                use_filesystem{ } filesystem{ ext4 }    \
                mountpoint{ / }                         \
                .                                       \
        512 1024 1024 linux-swap                        \
                $lvmok{ }                               \
                in_vg{ vg00 }                           \
                lv_name{ swap }                         \
                method{ swap } format{ }                \
                .                                       \
        64 128 8192 ext4                                \
                $lvmok{ }                               \
                in_vg{ vg00 }                           \
                lv_name{ deleteme }                     \
                method{ keep }                          \

# GlenPP's explanation:
#       * Create a /boot of 256MiB, high priority so this gets max
#       * Create a VG of the remainder of the disk
#       * Then create LVs:
#               * root 4GiB
#               * swap 1GiB
#               * deleteme to mop up free space - can't leave free space
#       * That leaves some free space in the VG for later

# This makes partman automatically partition without confirmation.
#d-i partman-md/confirm boolean true
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

# Implied anyway
#d-i partman-auto/choose_recipe      select boot-lvm
# This stops it questioning the unused (keep) volume
d-i partman-basicmethods/method_only boolean false
# You can choose to install non-free and contrib software.

d-i apt-setup/non-free boolean true
d-i apt-setup/contrib boolean true
# Uncomment this if you don't want to use a network mirror.
#d-i apt-setup/use_mirror boolean false
# Select which update services to use; define the mirrors to be used.
# Values shown below are the normal defaults.
#d-i apt-setup/services-select multiselect security, updates
#d-i apt-setup/security_host string

# Uncomment this to add multiarch configuration for i386
#d-i apt-setup/multiarch string i386

# what tasks to install
tasksel tasksel/first multiselect standard, mate-desktop, ssh-server

# Individual additional packages to install
#d-i pkgsel/include string openssh-server build-essential
# Whether to upgrade packages after debootstrap.
# Allowed values: none, safe-upgrade, full-upgrade
d-i pkgsel/upgrade select safe-upgrade

# For Ubuntu you will need this to avoid being asked
# Policy for applying updates. May be "none" (no automatic updates),
# "unattended-upgrades" (install security updates automatically), or
# "landscape" (manage system with Landscape).
#d-i pkgsel/update-policy select none

# Some versions of the installer can report back on what software you have
# installed, and what software you use. The default is not to report back,
# but sending reports helps the project determine what software is most
# popular and include it on CDs.
popularity-contest popularity-contest/participate boolean false

#This is fairly safe to set, it makes grub install automatically to the MBR
# if no other operating system is detected on the machine.
d-i grub-installer/only_debian boolean true

# Due notably to potential USB sticks, the location of the MBR can not be
# determined safely in general, so this needs to be specified:
#d-i grub-installer/bootdev  string /dev/sda
# To install to the first device (assuming it is not a USB stick):
d-i grub-installer/bootdev  string default

# Avoid that last message about the install being complete.
d-i finish-install/reboot_in_progress note

# This is how to make the installer shutdown when finished, but not
# reboot into the installed system.
#d-i debian-installer/exit/halt boolean true
# This will power off the machine instead of just halting it.
#d-i debian-installer/exit/poweroff boolean true

# Now for further automation you can run some scripts to do further neat stuff...

# This command is run just before the install finishes, but when there is
# still a usable /target directory. You can chroot to /target and use it
# directly, or use the apt-install and in-target commands to easily install
# packages and run commands in the target system.
#d-i preseed/late_command string apt-install zsh; in-target chsh -s /bin/zsh
# In our case we want to clean up our free space LV and install some extra packages
d-i preseed/late_command string lvremove --force /dev/mapper/vg00-deleteme; apt-install dash vim
# For Ubuntu if you want a desktop then also install "ubuntu-desktop" or other like minimal "lxde-core"

# More flexibly, this runs a shell command and if it outputs the names of
# preconfiguration files, includes those files.
#d-i preseed/include_command \
#      string if [ "`hostname`" = bob ]; then echo bob.cfg; fi

# Most flexibly of all, this downloads a program and runs it. The program
# can use commands such as debconf-set to manipulate the debconf database.
# More than one script can be listed, separated by spaces.
# Note that if the filenames are relative, they are taken from the same
# directory as the preconfiguration file that runs them.
#d-i preseed/run string

Obviously if you want a minimal (non-GUI) server for automation rather than Lab experimentation then just leave out the GUI parts.


Are you human? (reduces spam)
Note: Identity details will be stored in a cookie. Posts may not appear immediately