Preseeding - Fully Automated Install on Debian

See .../unattended-debian-installations-or-how-i-learned-to-stop-worrying-and-love-the-preseedcfg/
See also PXE boot and IPMI and SOL

It's now late 2012 and a couple more releases of Debian/Ubuntu and kickstart behaves reasonably. I'm leaving the original here, just in case these come back. Meanwhile, I believe you can ignore strike-throughs.

I've heard about RedHat Kickstart installations for some time. I've read there is even an effort to make them available on Debian, but not my version (Etch) as far as I know. Chasing Kickstart, however, led me to discover Debian's own technology called preseeding. This led me down a long tedious and highly error-filled road.

I'm not completely sure where all the problems came from. It might be that preseeding on etch is flat out broken, but works wonderfully elsewhere. It may be that preseeding is under going a large set of changes and not ready for prime time. In March 2008 I found many many examples on Google of preseeding config files, few of which actually worked even partially. It is certainly true that preseeding is filled with failures to document really really important things and this was the largest motivation for doing this web page. With luck I'll sort out more and more of the issues and someday this page will tell you how wonderfully easy preseeding is.

What follows are the details of what I found to work in my environment for my machines. Hopefully, someone else will find useful bits here. If nothing else, this is documentation for me for the next time.

The Goal

My goal was to be able to boot and more importantly install on a remote machine with no KVM and as little personal attention as possible. This means to bring up Serial Over LAN (SOL) session, initiate a boot (possible powercycle using IPMI), invoke PXE boot, select a particular menu item fron Syslinux, and get Debian to install itself asking no questions and finally reboot the machine. If all goes well, this sequence results in a new install on the target machine and a runnable system into which I can SSH. From there I can install packages, configure things and get the rest of the machine up and running.

I've covered the PXE boot, Syslinux, IPMI and SOL (serial over LAN) topics in other documents (see reference URLs at the top of the page). This page will touch on more details about SysLinux and preseeding.

Preseeding Configuration

One thing that makes this whole topic so confusing for me, was the interplay of kernel parameters and preseed directives. If there's a rule about what goes where, I never came across it. Somethings are in the preseed file, some are kernel parameters. Some can go either place, but for my case had to go in one place over the other.

The process seems straightforward enough. Boot an install kernel, tell it where the preseed file is and as the install scripts run, they get their 'answers' from the preseed file. If they don't have an 'answer', prompt for it (e.g. normal install interaction). How hard can that be?

One problem is that there are a really large set of preseed directives. Examples are great, but if what you need was not shown in the example or was wrong (abundant versions of both were found), you are out of luck. As a starter, check out http://www.debian.org/releases/etch/example-preseed.txt as a common example. I'm not going to copy it here, but rather I will explain all the nuanaces I discovered. Go ahead and click on that link - it'll open a new page. Resize the windows for this page and the other so you can see both.

Keep in mind, this page is all about a fully automated install. I wasn't trying to install anything complex. In fact I was attempting to install the absolute minimal Debian system, add in ssh and then reboot. Once the machine was installed and I could SSH into it, then a few scripts can do all the other configuration. My overriding concern, however, was that the install could ask no questions.

Kernel Parameters

Many times in the following sections, I'll note to 'pass xyz as a parameter to the kernel'. This means in your Syslinux default file (or whatever you call it), you have an entry like this (BEWARE: lines are broken for readability):

LABEL sata1950s
  MENU LABEL MOSIX AMD64 Client Automated Install  (1950s)
  kernel boot/debian/amd64/linux
  append vga=normal initrd=boot/debian/amd64/initrd.gz -- 
  append initrd=boot/debian/amd64/initrd.gz
    preseed/url=http://192.168.1.5/preseed/1950s.cfg
    KERNEL-PARAMETERS

This Syslinux menu item provides the initrd file and install kernel to boot. Tell the kernel where/how to read the preseed file. There are various ways to do this, so I read, but I chose to fetch the file from by private web server (also my Debian mirror). The file 1950s.cfg in this example started from an example and got modified extensively before it ended.

I'm ignoring parameters passed to the kernel (noted as KERNEL-PARAMETERS for now) that you might normally use. Those don't change. Many times they aren't actually needed. Some older kernels had limits on how long the KERNEL-PARAMETERS string could be. I saw references to 255 bytes here. My longest was 230 characters, so maybe there's another snake in the grass waiting for me yet.

Preseeding Surprises

 
# Locale sets language and country.
d-i debian-installer/locale string en_US

This is the first of a number of values that must get set as kernel parameters. You'd think it was enough to have this set in the preseed file, but not so. You must pass the string locale=en_US to the kernel or you'll be prompted for language.

 
# Keyboard selection.
d-i console-keymaps-at/keymap select us

Same story - it doesn't matter what's in the pressed file. You can even comment it out. You must pass the string console-keymaps-at/keymap=us to the kernel or you'll be prompted for the keyboard.

 
#debconf debconf/priority        string critical
#unknown debconf/priority        string critical
#d-i     debconf/priority        string critical

I never saw these documented. Maybe they are useful, maybe not. I passed the string debian/priority=critical to the kernel just to be sure.

 
# To pick a particular interface instead:
d-i netcfg/choose_interface select eth0

Nah - doesn't matter what you pick, you'll be prompted unless you pass the string netcfg/choose_interface=eth0 to the kernel.

 
# If you have a slow dhcp server and the installer times out waiting for it
d-i netcfg/dhcp_timeout string 60

Just to be sure, I saw this somewhere and included the string netcfg/dhcp_timeout=60 to the kernel. I should also mention here that my dhcp server provided the IP address, mask, gateway and DNS entries, so I never had to attempt to set a static address and such. I'd be afraid parts (all?) of the netcfg/get_* fields would need to be passed to the kernel too. I don't know.

 
# 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.
d-i netcfg/get_hostname string unassigned-hostname
d-i netcfg/get_domain string unassigned-domain
d-i netcfg/get_hostname seen true
d-i netcfg/get_domain seen true

Once again, set values in the preseed file, assign the hostname with dhcp, but you must pass the string hostname=x domain=x to the kernel.

 
#   Mirror settings

#   Bug 288053 makes it impossible to load a preseed file via http
#d-i mirror/codename string etch

#   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 US
#d-i mirror/ftp/hostname string ftp.us.debian.org
d-i mirror/ftp/hostname string 192.168.1.5
d-i mirror/ftp/directory string /debian/
d-i mirror/ftp/proxy string

This took me hours to sort out. My version simply would not use the protocol http. Fortunately, since I had my own mirror, I could set up an FTP server. Once I started using FTP, things started to work. Now, several releases later, http behaves as I wanted.

 
#   Here begins syntax hell
#       If you get anything slightly wrong you'll get either one large partition
#       or NO partitions.  Never will you see an error message.
#   A few things to remember:
#       Period '.' at the end of the line is really important. Many examples skip it
#       Priority (second number) APPEARs to be zero based.  10 is better than 20
#       There must be other rules, but they are POORLY documented if at all
#
#   Intention is to create.  This sort of works, but it was impossibly difficult
#       sda3: rest of drive for /tmp
#       sda2: 16G+ for swap
#       sda1: rest for  /
d-i partman-auto/expert_recipe string                         \
      boot-root ::                                            \
              30000 10000 30000 ext4                          \
                      $primary{ } $bootable{ }                \
                      method{ format } format{ }              \
                      use_filesystem{ } filesystem{ ext4 }    \
                      mountpoint{ / }                         \
              .                                               \
              32000 10000 200% linux-swap                     \
                      $primary{ }                             \
                      method{ swap } format{ }                \
              .                                               \
              20000 10000 2000000000000 xfs                   \
                      $primary{ }                             \
                      method{ format } format{ }              \
                      use_filesystem{ } filesystem{ xfs }     \
                      mountpoint{ /tmp }                      \
              .

# This makes partman automatically partition without confirmation, provided
# that you told it what to do using one of the methods above.
d-i partman/confirm_write_new_label 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 false

After another marathon session, I finally got partitioning to do SOMETHING useful. Unless you have minimal requirements for partitioning, you will waste lots of time here. I saw lots of examples of this section, none of them (except one) ever worked. Somewhere (I've lost the URL), the syntax for partman-auto is explained. It made no sense, nor behaved as explained, so maybe losing the URL was no loss.

 
#   Select the initramfs generator used to generate the initrd for 2.6 kernels.
#d-i base-installer/kernel/linux/initramfs-generators string yaird

This works for my i386 systems and amd64 on SC1425s and PE1850s. It fails for amd64 on PE1950s. I can't even dream why. Now much later: I haven't used this in years now. Comment it out without loss as far as I can tell.

 
d-i pkgsel/include string ssh rsync initrd-tools cramfsprogs lzop

One very pleasant surprise was the ability to install other packages. Since I install a minimal system, I need ssh installed so I can get in. Doing this manually, I always had to reboot, login and THEN install ssh. With preseeding, I can specify a set of packages to be installed before that reboot, so the system comes up ready for me.

 
#### Advanced options
### Running custom commands during the installation
# d-i preseeding is inherently not secure. Nothing in the installer checks
# for attempts at buffer overflows or other exploits of the values of a
# preconfiguration file like this one. Only use preconfiguration files from
# trusted locations! To drive that home, and because it's generally useful,
# here's a way to run any shell command you'd like inside the installer,
# automatically.

# This first command is run as early as possible, just after preseeding is read.
#d-i preseed/early_command string anna-install some-udeb

# 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
#d-i preseed/late_command string in-target echo "############# START late commands#########"; \
  in-target /usr/bin/wget -O /tmp/p.deb http://192.168.1.251/csg/pool/lucid/local/csgbootstrap-1.0_1_amd64.deb; \
  in-target dpkg -i /tmp/p.deb; \
  in-target /usr/bin/wget -O /tmp/p.deb http://192.168.1.251/csg/pool/lucid/local/radmind-client_1.0-1_amd64.deb; \
  in-target dpkg -i /tmp/p.deb; \
  echo  "############# END late commands #########"

Another very useful surprise were these early- and late- commands. They are all commented out here, but there was a time when they were right handy. In this case I used wget to fetch a couple of local package files and install them (copy to /tmp/p.deb and then install that file). It was a bit of a hack, but it did allow me to do even more automatic configuring. Today we use puppet for all this stuff - a far superior tool.

Conclusion

The base-installer/kernel/linux/initramfs-generators statement was such a let down. I can install SC1425s, IBM Blades and PE1850s just like I wanted. My PE1950s require me to wait and answer a few questions. So close, yet so far. This actually gives me hope - that I was able to actually do a fully automated install, but only for certain machines. I remain hopeful this was just caused by bugs that will get fixed and someday I'll have what I really wanted.

Now much later, kickstart works pretty well. Having spent a gigantic amount of time trying to get to work the first times, I don't mess with what I do have. We recently upgraded from Ubuntu Lucid to Ubuntu Precise and I must admit I was simply shocked the same kickstart file actually worked. Having been traumatized years before, I was prepared to waste days and days guessing the new set of bugs. It appears kickstart is stable (cross your fingers). YMMV.

StartUp Config

One good thing to come of this was I learned how to PXE boot an install kernel and let it prompt me as usual. There are some things I never need prompting for, so I created this 'startup.cfg' preseed configuration.

d-i debian-installer/locale string en_US
d-i console-tools/archs select at
d-i console-keymaps-at/keymap select American English
d-i debian-installer/keymap string us
d-i netcfg/get_hostname string unassigned-hostname
d-i netcfg/get_hostname seen true
d-i netcfg/get_domain string unassigned-domain
d-i netcfg/get_domain seen true
d-i mirror/protocol string ftp
d-i mirror/ftp/hostname string 192.168.1.5
d-i mirror/ftp/directory string /debian/
d-i mirror/ftp/proxy string
d-i clock-setup/utc boolean true
d-i time/zone string US/Eastern
d-i clock-setup/ntp boolean true
popularity-contest popularity-contest/participate boolean false
d-i pkgsel/include string ssh rsync initrd-tools cramfsprogs lzop
d-i passwd/make-user boolean false
d-i grub-installer/only_debian boolean true

Complete i386 Preseed Config (Appears to Work)

Beware, long lines were formatted for the web, not for the installer.

d-i debian-installer/locale string en_US
d-i console-tools/archs select at
d-i console-keymaps-at/keymap select American English
d-i debian-installer/keymap string us
d-i netcfg/choose_interface select eth0
d-i netcfg/get_hostname string unassigned-hostname
d-i netcfg/get_hostname seen true
d-i netcfg/get_domain string unassigned-domain
d-i netcfg/get_domain seen true
d-i mirror/protocol string ftp
d-i mirror/ftp/hostname string 192.168.1.5
d-i mirror/ftp/directory string /debian/
d-i mirror/ftp/proxy string
d-i clock-setup/utc boolean true
d-i time/zone string US/Eastern
d-i clock-setup/ntp boolean true
d-i passwd/root-password password PASSWORD
d-i passwd/root-password-again password PASSWORD
d-i passwd/make-user boolean false
popularity-contest popularity-contest/participate boolean false
d-i partman-auto/method string regular
d-i partman-auto/disk string /dev/hda
d-i partman-auto/choose_recipe select All files in one partition (recommended for new users)
d-i	partman-auto/expert_recipe string exampleserver :: 15000 50 20000 ext3
   $primary{ } $bootable{ } method{ format } format{ }
   use_filesystem{ } filesystem{ ext3 } mountpoint{ / }
   . 64 512000 3000% linux-swap method{ swap } format{ } .
d-i partman/confirm_write_new_label boolean true
d-i partman/choose_partition select Finish partitioning and write changes to disk
d-i partman/confirm boolean true
d-i base-installer/kernel/linux/initramfs-generators string yaird
d-i base-installer/kernel/image linux-image-2.6-486
d-i apt-setup/local0/repository string http://192.168.1.5/debian etch main
tasksel tasksel/first multiselect standard
d-i pkgsel/include string ssh rsync initrd-tools cramfsprogs lzop
d-i grub-installer/only_debian boolean true
d-i grub-installer/with_other_os boolean true
d-i finish-install/reboot_in_progress note

Complete amd64 Preseed Config (OK on SC1425 and PE1850)

Beware, long lines were formatted for the web, not for the installer.

d-i debian-installer/locale string en_US
d-i console-tools/archs select at
d-i console-keymaps-at/keymap select American English
d-i debian-installer/keymap string us
d-i netcfg/choose_interface select eth0
d-i netcfg/get_hostname string unassigned-hostname
d-i netcfg/get_hostname seen true
d-i netcfg/get_domain string unassigned-domain
d-i netcfg/get_domain seen true
d-i mirror/protocol string ftp
d-i mirror/ftp/hostname string 192.168.1.5
d-i mirror/ftp/directory string /debian/
d-i mirror/ftp/proxy string
d-i clock-setup/utc boolean true
d-i time/zone string US/Eastern
d-i clock-setup/ntp boolean true
d-i passwd/root-password password PASSWORD
d-i passwd/root-password-again password PASSWORD
d-i passwd/make-user boolean false
popularity-contest popularity-contest/participate boolean false
d-i partman-auto/method string regular
d-i partman-auto/disk string /dev/sda
d-i partman-auto/choose_recipe select All files in one partition (recommended for new users)
d-i	partman-auto/expert_recipe string exampleserver :: 35000 50 20000 ext3
  $primary{ } $bootable{ } method{ format } format{ }
  use_filesystem{ } filesystem{ ext3 } mountpoint{ / } 
  . 64 512000 3000% linux-swap method{ swap } format{ } .
d-i partman/confirm_write_new_label boolean true
d-i partman/choose_partition select Finish partitioning and write changes to disk
d-i partman/confirm boolean true
d-i base-installer/kernel/linux/initramfs-generators string yaird
d-i base-installer/kernel/image linux-image-2.6-amd64
d-i apt-setup/local0/repository string http://192.168.1.5/debian etch main
tasksel tasksel/first multiselect standard
d-i pkgsel/include string ssh rsync initrd-tools cramfsprogs lzop
d-i grub-installer/only_debian boolean true
d-i grub-installer/with_other_os boolean true
d-i finish-install/reboot_in_progress note

Complete amd64 Preseed Config (Mostly OK on PE1950)

d-i debian-installer/locale string en_US
d-i console-tools/archs select at
d-i console-keymaps-at/keymap select American English
d-i debian-installer/keymap string us
d-i netcfg/get_hostname string unassigned-hostname
d-i netcfg/get_hostname seen true
d-i netcfg/get_domain string unassigned-domain
d-i netcfg/get_domain seen true
d-i mirror/protocol string ftp
d-i mirror/ftp/hostname string 192.168.1.5
d-i mirror/ftp/directory string /debian/
d-i mirror/ftp/proxy string
d-i clock-setup/utc boolean true
d-i time/zone string US/Eastern
d-i clock-setup/ntp boolean true
popularity-contest popularity-contest/participate boolean false
d-i pkgsel/include string ssh rsync initrd-tools cramfsprogs lzop
d-i passwd/root-password password PASSWORD
d-i passwd/root-password-again password PASSWORD
d-i passwd/make-user boolean false
d-i grub-installer/only_debian boolean true