3.7 Unattended and Automated Installs: Kickstart and Preseed
Right, so you’re tired of babysitting an installer. I don’t blame you. Clicking “next” for the tenth time while it asks you about your timezone for the third time is a special kind of hell. This is where we automate ourselves to freedom using either Kickstart (for the Red Hat, Fedora, CentOS crowd) or Preseed (for the Debian/Ubuntu devotees).
The core idea is beautifully simple: you craft a single, plain text file that answers every question the installer would ever ask. You then point the installer at this file, go get a coffee, and come back to a fully installed system. It’s like teaching a very obedient, very fast intern how to do your job.
The Anatomy of a Kickstart File
A Kickstart file (ks.cfg by convention) is essentially a script for the Anaconda installer. You can generate one easily by doing a manual install on a reference machine. Once you’re done, Anakonda drops a /root/anaconda-ks.cfg file for you. It’s a fantastic starting point, but you’ll want to edit it. Heavily.
Here’s a trimmed-down but fully functional example. Let’s walk through the critical bits.
# Platform-specific action. BIOS vs UEFI is the great schism of our time.
# If you're on UEFI, you MUST have this, or it'll fall back to BIOS.
# If you're on old-school BIOS, this will break everything. You've been warned.
zerombr
clearpart --all --initlabel
autopart
# This is non-negotiable. Without a reboot, you'll get a half-baked, unbootable mess.
reboot
# The most important line. This is where you tell it *what* to install.
# The @^ is RPM group notation. This gives us a minimal GUI environment.
%packages
@^minimal-environment
vim-enhanced
git
%end
# This is where you do your post-install dirty work.
%post --log=/root/post-install.log
#!/bin/bash
# Update all the things immediately
dnf update -y
# Set a custom MOTD because why not
echo "Welcome to my meticulously automated server." > /etc/motd
# Let's get fancy: create a user and set their password without interactive prompts
# Note: This creates a password hash in a... less than secure way. We'll fix that later.
useradd -m -G wheel myuser
echo "myuser:mySuperSecretPassword123" | chpasswd
%end
The %post section is your playground. It’s a bash script that runs in a chroot of the new system. Anything you can script, you can do here: install custom packages, pull configs from a Git repo, register with your configuration management tool, you name it.
Debian’s Flavor: Preseed Files
Debian’s installer, debian-installer, uses a method called preseeding. The concept is identical to Kickstart, but the syntax is different, reflecting Debian’s configuration philosophy. It’s a series of question = answer pairs.
You can’t generate a full preseed file from a finished system, which is a bit of a pain. You often have to comb through the installer source to find the exact question names. Thankfully, the Debian docs provide excellent examples.
The real magic is in how you get this file to the installer. The most reliable method is to pack it into a custom initrd or serve it over HTTP. Here’s a snippet that handles some common, tricky questions.
# Tell the installer to automatically load the preseed file from the web
# This goes in your kernel boot arguments (in your GRUB config or PXE menu)
url=http://my-server/path/to/preseed.cfg
# --- Contents of preseed.cfg ---
# Critical: Choose the mirror and avoid prompts about non-free firmware
d-i mirror/country string manual
d-i mirror/http/hostname string http.us.debian.org
d-i mirror/http/directory string /debian
d-i mirror/http/proxy string
# This is the big one. Without this, it'll stop and ask.
d-i partman-auto/method string lvm
# Automatically agree to wiping the disk. DANGER: This will nuke the first disk it finds.
d-i partman-auto/disk string /dev/sda
d-i partman-lvm/device_remove_lvm boolean true
d-i partman-md/device_remove_mdm boolean true
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true
d-i partman-auto-lvm/guided_size string max
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
# Set the root password without a prompt. The [MODE] is the hash type.
# Use `openssl passwd -6` to generate a proper SHA-512 hash. Do NOT use plain text.
d-i passwd/root-password password r00t!myPassword
d-i passwd/root-password-again password r00t!myPassword
The Pitfalls and How to Avoid Them
The Test Loop is Your Life Now. You will get this wrong the first five times. Do not test on physical hardware. Use a VM and take snapshots. Test the installation from start to finish. A syntax error in your
%postscript means you’re debugging viachrootfrom a rescue shell. It’s not fun.Password Problems. Did you see how I just put a plain text password in that Kickstart example? That’s terrible. Never do that. For Kickstart, always generate a password hash and use the
--iscryptedflag.# Generate a shadow hash openssl passwd -6 "myPassword" # Use it in ks.cfg rootpw --iscrypted $6$2Bpfb1jHLgT2Fp$X5L9bL...snip...g7lY0For Preseed, use
openssl passwd -6and put the entire hash in the preseed file.The Horror of Partitioning. This is the number one reason automated installs fail. The installer will happily follow your orders to wipe the wrong disk. Be hyper-specific. Use disk-by-id (
/dev/disk/by-id/) in your preseed or Kickstart files whenever possible, especially in real hardware environments. It’s the only way to be sure.Network or It Doesn’t Work. Your
%postscript needs a network. If your automated install is failing mysteriously at the end, it’s often because the%postscript can’t resolve DNS or reach your internal repos. Test your network commands from inside thechrootfirst.
The goal isn’t just to install one machine. It’s to create a reproducible, version-controlled artifact (ks.cfg or preseed.cfg) that defines your system’s baseline. From there, configuration management takes over. This is the foundation. Get it right, and you can rebuild your entire infrastructure without breaking a sweat.