Automated Linux installs: Kickstart, preseed and autoinstall
An automated install answers every installer question from a text file, so ten or a thousand machines come out identical. Red Hat-style distributions read a Kickstart file, Debian reads a preseed file, and Ubuntu reads an autoinstall YAML file. You put the file on a web server or USB stick and add one option to the installer's kernel command line.
The old KickStart HOWTO (1999) covered Red Hat Linux 5 and boot floppies. The idea is unchanged, but the commands are not. This page uses AlmaLinux 10 as the Red Hat example. The same Kickstart works on RHEL 10 and Rocky Linux 10, and nearly the same file works on Fedora.
| Distribution | Installer | File | Boot option that loads it |
|---|---|---|---|
| RHEL, AlmaLinux, Rocky 9–10, Fedora | Anaconda | Kickstart (ks.cfg) | inst.ks=URL |
| Debian 13 | debian-installer | preseed (preseed.cfg) | auto url=URL |
| Ubuntu Server 20.04+, Desktop 23.04+ | Subiquity | autoinstall (user-data or autoinstall.yaml) | autoinstall ds=nocloud-net;s=URL/ |
Kickstart: start from a real install
The easiest first Kickstart file is the one Anaconda writes for you. After any manual install, the installed system has /root/anaconda-ks.cfg with every choice you made. Copy it, edit it, and check it. It contains password hashes, so keep its permissions tight.
Red Hat customers can also use the Kickstart Generator on the Customer Portal, and the Kickstart Converter to update RHEL 9 files for RHEL 10. Both need a portal account.
A complete minimal server Kickstart
This file installs a minimal AlmaLinux 10 server from the public mirror, wipes disk sda, and leaves you with an admin user who logs in with an SSH key. Replace the hash, key and host name with your own.
# ks.cfg – AlmaLinux 10 minimal server (works on RHEL/Rocky 10 with a different url/repo)
text
url --url=https://repo.almalinux.org/almalinux/10/BaseOS/x86_64/os/
repo --name=AppStream --baseurl=https://repo.almalinux.org/almalinux/10/AppStream/x86_64/os/
lang en_US.UTF-8
keyboard --vckeymap=us
timezone America/New_York --utc
timesource --ntp-pool=pool.ntp.org
network --bootproto=dhcp --device=link --activate --hostname=web01.example.com
rootpw --lock
user --name=admin --groups=wheel --iscrypted --password=$6$REPLACE_WITH_HASH
sshkey --username=admin "ssh-ed25519 AAAAC3...REPLACE admin@laptop"
selinux --enforcing
firewall --enabled --ssh
ignoredisk --only-use=sda
zerombr
clearpart --all --initlabel --drives=sda
autopart --type=lvm
%packages
@^minimal-environment
vim-enhanced
%end
%post --log=/root/ks-post.log
dnf -y update
%end
reboot
Make the password hash with openssl passwd -6; it asks for the password and prints a $6$… string (SHA-512 crypt). What the main lines do:
urlandrepo- Where packages come from. For a RHEL install from the DVD, use
cdrominstead. --device=link- Use the first network card whose link is up, so the same file works on different hardware.
rootpw --lock- Lock the root account; no password is needed with
--lock. Members ofwheeluse sudo. zerombr,clearpart- Destroy what is on the disk without asking.
ignoredisk --only-use=sdakeeps other disks (and USB sticks) safe. autopart --type=lvm- Create the boot, root and swap volumes the platform needs. You cannot combine it with
part,reqpartorlogvol. @^minimal-environment@^picks an environment group (here "Minimal Install");@nameis a group and a plain name is a package. A leading-removes a package.%post- A shell script that runs inside the new system (chroot) after installation. Every
%packages,%preand%postsection must end with%end.
Check the file before you boot
sudo dnf install pykickstart
ksvalidator -v RHEL10 ks.cfg
No output means the syntax is valid. ksvalidator also fails on deprecated commands, but it does not check what your %pre, %post and %packages sections do. Test in a virtual machine before you point it at real disks.
Old files need editing for RHEL 10. auth/authconfig has been removed (use the authselect command instead), timezone --ntpservers is no longer accepted (use the timesource command), and btrfs and method are gone. vnc is deprecated and no longer has any effect; remote graphical installs use rdp. Run ksvalidator on every RHEL 8 or 9 file before reuse.
Tell the installer where the file is
At the installer's boot menu, press e (GRUB menu) or Tab (older isolinux menus), add one option to the kernel line, and boot (Ctrl+x in GRUB). In a PXE menu, put the option in the configuration file instead:
inst.ks=http://192.168.10.1/ks/web01.cfg # web server
inst.ks=nfs:192.168.10.1:/exports/ks/web01.cfg # NFS
inst.ks=hd:UUID=1234-ABCD:/ks.cfg # a local or USB volume
Without any option, Anaconda also looks for a volume labeled OEMDRV with ks.cfg in its root. Label a USB stick and copy the file to it:
sudo fatlabel /dev/sdX1 OEMDRV # FAT (or e2label for ext4, xfs_admin -L for XFS)
Before RHEL 7 and Fedora 17 the option was spelled ks=; old guides that use it are out of date.
Debian preseed
Preseeding fills in the answers to debian-installer's questions. Start from the official example for Debian 13 and remove the # from the lines you need:
wget https://www.debian.org/releases/stable/example-preseed.txt -O preseed.cfg
A short working core, for one disk with LVM:
d-i debian-installer/locale string en_US.UTF-8
d-i keyboard-configuration/xkb-keymap select us
d-i netcfg/choose_interface select auto
d-i netcfg/get_hostname string web02
d-i netcfg/get_domain string example.com
d-i mirror/country string manual
d-i mirror/http/hostname string deb.debian.org
d-i mirror/http/directory string /debian
d-i mirror/http/proxy string
d-i passwd/root-login boolean false
d-i passwd/user-fullname string Admin
d-i passwd/username string admin
d-i passwd/user-password-crypted password $6$REPLACE_WITH_HASH
d-i clock-setup/utc boolean true
d-i time/zone string America/New_York
d-i partman-auto/disk string /dev/sda
d-i partman-auto/method string lvm
d-i partman-auto-lvm/guided_size string max
d-i partman-lvm/device_remove_lvm boolean true
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true
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
tasksel tasksel/first multiselect standard, ssh-server
d-i grub-installer/only_debian boolean true
d-i grub-installer/bootdev string default
d-i finish-install/reboot_in_progress note
d-i preseed/late_command string in-target apt-get -y install sudo
Boot the installer and choose Advanced options → Automated install, or type the parameters yourself:
auto url=http://192.168.10.1/d-i/preseed.cfg
# where the "auto" menu entry is missing:
auto=true priority=critical url=http://192.168.10.1/d-i/preseed.cfg
Auto mode delays the language, country and keyboard questions until the network is up, so the file can answer them. You can also put preseed.cfg in the root of the initrd, which works with every install method but means rebuilding the initrd. Preseeding cannot reuse existing partitions: it must partition a whole disk or its free space.
To find the name of a question you did not cover, do one manual install and run debconf-get-selections --installer (from the debconf-utils package) on the result.
Ubuntu autoinstall
Ubuntu's Subiquity installer reads a YAML file. It differs from preseed in one important way: if an autoinstall file is present at all, every question you did not answer takes its default instead of stopping to ask. The most common way to deliver it is as cloud-init user-data:
#cloud-config
autoinstall:
version: 1
identity:
hostname: web03
username: admin
password: "$6$REPLACE_WITH_HASH"
ssh:
install-server: true
authorized-keys:
- ssh-ed25519 AAAAC3...REPLACE admin@laptop
allow-pw: false
storage:
layout:
name: lvm
packages:
- vim
late-commands:
- curtin in-target -- apt-get -y upgrade
The first line, #cloud-config, is required. Serve the file next to an empty meta-data file and point the installer at the directory (keep the trailing slash; in a GRUB menu, escape the semicolon as \; because GRUB treats a bare ; as a command separator):
mkdir -p ~/www && cd ~/www # put user-data here
touch meta-data
python3 -m http.server 3003
# installer kernel command line:
autoinstall ds=nocloud-net\;s=http://192.168.10.1:3003/
Other options: copy autoinstall.yaml (without the #cloud-config wrapper) to the root of the USB stick, next to the casper folder, or build a small seed ISO with cloud-localds seed.iso user-data meta-data from cloud-image-utils. Without the autoinstall keyword on the command line, the installer asks for confirmation before it erases the disk. The lvm layout uses the largest disk unless you add a match: rule.
Network installs with PXE and iPXE
For real automation, boot the installer from the network too. The firmware gets an address and a boot file name from DHCP, downloads a boot loader over TFTP or HTTP, and that boot loader fetches the installer kernel and initrd. iPXE makes this easy because it can load everything over HTTP. With UEFI Secure Boot, use the signed iPXE build from the iPXE releases (ipxe-shim.efi) and the distribution's shim, as described on ipxe.org.
An iPXE script that installs AlmaLinux 10 with the Kickstart above:
#!ipxe
dhcp
set repo https://repo.almalinux.org/almalinux/10/BaseOS/x86_64/os
kernel ${repo}/images/pxeboot/vmlinuz inst.repo=${repo} inst.ks=http://192.168.10.1/ks/web01.cfg
initrd ${repo}/images/pxeboot/initrd.img
shim ${repo}/EFI/BOOT/BOOTX64.EFI
boot
The shim line does nothing when Secure Boot is off, so it is safe to keep. For Debian, use the netboot.tar.gz image from the Debian mirror (under dists/trixie/main/installer-amd64/current/images/netboot/); for Ubuntu, the casper/vmlinuz and casper/initrd files from the live server ISO. DHCP and TFTP setup with dnsmasq is shown in the Diskless HOWTO.
Mistakes to avoid
- Never leave an automated, disk-wiping boot entry as the default on a network where other machines PXE boot. Make it a menu choice or tie it to MAC addresses.
- Do not put plain-text passwords in files on a web server. Use hashes and SSH keys, and remove the files when you are done.
- Name the target disk (
ignoredisk --only-use=,partman-auto/disk,match:). Device names such assdacan change when a USB stick is plugged in. - Test every change in a virtual machine first.
The classic 1999 KickStart HOWTO is no longer kept on tldp.org. For disk layouts, see the Partition mini-HOWTO.
Sources
- Kickstart documentation, pykickstart
- Automatically installing RHEL (RHEL 10), Red Hat
- Debian installation guide, Appendix B: Automating the installation using preseeding
- B.2 Using preseeding (boot parameters, auto mode), Debian
- Example preseed file for Debian 13, debian.org
- Introduction to autoinstall, Ubuntu installation documentation
- Providing autoinstall configuration, Ubuntu
- Autoinstall quick start, Ubuntu
- Autoinstall configuration reference, Ubuntu
- UEFI Secure Boot, iPXE and kernel command, iPXE
- openssl-passwd(1), OpenSSL
- AlmaLinux 10 BaseOS installation tree