Kickstart is the answer-file format read by Anaconda, the RHEL/CentOS/Fedora installer. A single .ks file answers every question the interactive installer would otherwise ask: language, keyboard, network, partitioning, package selection, users, and arbitrary post-install shell commands.
Anatomy of a Kickstart file¶
# Minimal server kickstart
lang en_US.UTF-8
keyboard us
timezone America/Toronto --utc
# Networking — see the "Networking options" section below for the boot-time ip= form;
# this network line configures the *installed system's* interface
network --bootproto=dhcp --device=eth0 --onboot=on --hostname=server01
rootpw --iscrypted $6$roundsaltedhashhere
authselect --enableshadow --passalgo=sha512
# Partitioning
zerombr
clearpart --all --initlabel
part /boot --fstype=xfs --size=1024
part swap --size=4096
part / --fstype=xfs --grow --size=1
bootloader --location=mbr
reboot
%packages
@core
chrony
vim-enhanced
%end
%post
systemctl enable chronyd
echo "Kickstart complete" > /etc/motd
%end
The full command reference is large enough that reading it in the source is more reliable than any single cheat sheet — clone Anaconda and read the command definitions directly:
git clone https://github.com/rhinstaller/anaconda
cd anaconda
${EDITOR:-vi} pyanaconda/core/kickstart/commands.py
Complex partitioning¶
Kickstart's partitioning commands (part, logvol, raid, volgroup) cover LVM and software RAID directly. dark.ca's complex partitioning writeup remains a useful worked-example reference for combining LVM and RAID in one file, beyond what the man page's simple examples show.
Generate a starting point from a real install¶
Every manual RHEL/CentOS install writes the choices it was given back out as a kickstart file in /root/:
ls /root/anaconda-ks.cfg /root/original-ks.cfg
# anaconda-ks.cfg — as-run, reflects any last-minute changes made during install
# original-ks.cfg — the file the installer started from, if one was supplied
Copying and trimming anaconda-ks.cfg from a hand-installed reference machine is usually faster than writing one from scratch. Red Hat also publishes an online Kickstart generator (subscriber login required) that walks through the same options interactively.
Validation and tooling: pykickstart¶
sudo dnf install pykickstart
| Tool | Purpose |
|---|---|
ksvalidator file.ks |
lint a kickstart file for syntax errors before you rely on it |
ksverdiff -f rhel7 -t rhel8 |
show what changed in available commands between two RHEL major versions |
ksflatten |
resolve %include directives into a single flat file (see man page) |
ksshell |
interactive kickstart shell for exploring commands (see man page) |
Always run ksvalidator on a file before pointing a boot at it — a syntax error partway through partitioning can leave a machine in an awkward half-wiped state.
Making a Kickstart file available to the installer¶
Anaconda fetches the file via the inst.ks= boot parameter (older syntax: ks=), with several source options:
# HTTP(S) — the common case, works well with the PXE/netboot page in this section
linux inst.ks=http://<server>/kickstart/<file>.cfg <networking_options>
# From a Git-hosted raw file (GitLab, GitHub, or a self-hosted instance)
linux inst.ks=https://git.example.com/<user>/kickstart/raw/master/server_ks.cfg <networking_options>
# From the install CD/DVD itself
linux inst.ks=cdrom:/dev/cdrom:/ks/<file>.cfg <networking_options>
# From an attached floppy image (still relevant for some VMware setups)
linux inst.ks=hd:fd0:/<file>.cfg <networking_options>
# From a second disk/image (e.g. a Dell iDRAC-mounted virtual media image)
linux inst.ks=hd:/dev/sdb:/<file>.cfg <networking_options>
Fetching from a private Git repository over the GitLab API¶
If the kickstart lives in a private repo, use a personal access token against the GitLab API rather than embedding a plain password, and prefer passing the token as a header/query param over one baked permanently into a kickstart URL that might get logged:
# Find the project ID
curl "https://git.example.com/api/v4/projects?search=kickstart&private_token=<token>"
# Find the file's path within the project (GitLab v4 API takes a URL-encoded path, not a numeric file ID)
curl "https://git.example.com/api/v4/projects/<project_id>/repository/tree?private_token=<token>"
# Fetch the raw file content directly with inst.ks
linux inst.ks="https://git.example.com/api/v4/projects/<project_id>/repository/files/<url_encoded_path>/raw?ref=main&private_token=<token>" <networking_options>
(The GitLab v3 API used a numeric raw_blobs/<file_id> endpoint; v3 has been removed from current GitLab releases; the v4 API above uses the URL-encoded file path instead of a blob ID.) A deploy token scoped read-only to that one repo is safer here than a personal access token, since the URL — and therefore the token — is visible in kernel boot logs and DHCP/PXE server logs.
Building a custom install ISO with the kickstart embedded¶
KS_DIR="/build/RHEL/isolinux/ks"
mkdir -p "$KS_DIR"
cp <kickstart_file>.cfg "$KS_DIR/ks.cfg"
sudo mkisofs -o <hostname>.iso -b isolinux.bin -c boot.cat -no-emul-boot \
-V 'RHEL x86_64' -boot-load-size 4 -boot-info-table -R -J -v \
-T /build/RHEL/isolinux/
Networking options at boot time¶
These are separate from the network command inside the kickstart itself — they tell the installer how to reach the network before it can even fetch an HTTP-hosted kickstart file. Full reference: Anaconda boot options — network options.
ip=<ip>::<gateway>:<netmask>:<hostname>:<interface>:none nameserver=<nameserver_ip>
# example
ip=192.0.2.50::192.0.2.1:255.255.255.0:server01:ens160:none nameserver=192.0.2.1
# dhcp instead of a static address
ip=dhcp
A bonded-interface installer target¶
linux inst.ks=http://<server>/ks/<file>.cfg ip=dhcp bond=bond0:ens160,ens192:mode=802.3ad,primary=ens160
Debugging a kickstart install over SSH¶
Adding inst.sshd=1 starts sshd inside the installer environment itself, letting you shell in mid-install to inspect logs or a partial partition layout before it reboots.
sudo virt-install --name test-vm --memory 1024 --vcpus 1 \
--location ~/rhel-boot.iso --os-variant rhel9 \
--disk pool=default,bus=virtio,size=10 \
--network bridge=br0,model=virtio \
--graphics none --console pty,target_type=serial \
--extra-args "console=ttyS0,115200n8 inst.sshd=1 inst.ks=http://192.0.2.50:8000/standard_ks.cfg ip=dhcp"
Find the DHCP lease the installer picked up by diffing the hypervisor's ARP table before and after boot:
arp -an > /tmp/leases_before.txt
# ... start the install, wait for it to reach the point of acquiring a DHCP lease ...
arp -an > /tmp/leases_after.txt
diff /tmp/leases_before.txt /tmp/leases_after.txt
Then connect — no password should be required inside the installer environment:
ssh root@<installer_ip>
virsh net-dhcp-leases <network> is usually a cleaner source of the lease than ARP-table diffing if the VM is on a libvirt-managed network, since it shows the lease directly rather than requiring a before/after snapshot.
See also: PXE/netboot for serving the kickstart and boot files together without per-VM --extra-args editing, and Cobbler/Foreman for managing many kickstart profiles instead of hand-editing URLs per host.