The scenario
You have twelve identical servers to provision. The manual installer asks for keyboard layout, disk partitioning, root password, and package selection on every single machine. You sit through the same ten screens twelve times. You make a typo on the eighth machine. The deployment takes three hours instead of forty minutes. You need a way to feed the installer a single source of truth and walk away.
What Kickstart actually does
The Fedora installer is called Anaconda. Anaconda normally waits for human input at every stage. Kickstart is a plain text configuration file that pre-answers every question Anaconda could possibly ask. You write the file once. Anaconda reads it on boot and executes the installation without showing a single dialog.
Think of it like filling out a tax form with a macro. The macro types every field, checks every box, and hits submit. The installer still runs the same validation checks, formats the same partitions, and installs the same packages. The only difference is that a text file provides the answers instead of a keyboard.
The file uses a simple directive format. Each line tells Anaconda what to do. Lines starting with a hash are comments. Blank lines are ignored. The order of directives matters less than you would expect, but grouping them logically makes debugging easier. Anaconda parses the entire file before touching the disk. If it finds a fatal syntax error, it aborts before formatting anything.
Building a working ks.cfg
Start with a minimal template. You can generate one from an existing Fedora system by running system-config-kickstart or by exporting the configuration from a freshly installed machine. For most server deployments, you only need partitioning, network, authentication, and package selection.
Here is how to structure a base configuration file for a headless server.
#platform=x86_64
# Anaconda reads this line to confirm architecture compatibility
# It does not change behavior but helps documentation tools parse the file
lang en_US.UTF-8
# Sets the system locale for console and installed packages
keyboard us
# Defines the default keyboard mapping for the installed system
rootpw --plaintext mysecurepassword
# Sets the root password. Use --iscrypted in production with a hashed value
firewall --enabled --service=ssh
# Enables nftables/firewalld and opens port 22 for remote management
auth --enableshadow --passalgo=sha512
# Configures PAM to use shadow passwords and SHA-512 hashing
timezone UTC --utc
# Sets system clock to UTC. Hardware clocks on servers should always use UTC
reboot
# Tells Anaconda to restart automatically after installation completes
network --bootproto=dhcp --device=eth0 --onboot=on
# Configures the primary interface. Replace eth0 with your actual device name
# Use --ipv6=auto if your network requires dual-stack addressing
clearpart --all --initlabel
# Wipes existing partitions and creates a fresh GPT or MBR label
part /boot --fstype="xfs" --size=1024
# Creates a dedicated boot partition. 1GB handles multiple kernel versions
part / --fstype="xfs" --grow --size=1024
# Root partition grows to fill remaining disk space
part swap --fstype="swap" --size=4096
# Swap partition sized for hibernation or memory overflow scenarios
%packages
@^minimal-environment
# Installs the base server group without desktop dependencies
@server-productivity
# Adds standard server utilities and documentation
@system-admin-tools
# Provides sysstat, iproute2, and other administration packages
%end
Keep the %packages section tight. Every extra package increases download time, expands the attack surface, and slows down the initial boot. Fedora's package groups use the @ prefix. The ^ symbol marks an environment group, which pulls in recommended dependencies automatically. If you need to pin specific package versions or exclude problematic dependencies, list them individually below the group declarations.
If you need to run commands after the base system is installed but before the first reboot, add a %post section. The %post script runs inside a chroot environment. It has access to dnf, systemctl, and standard shell utilities. The chroot mounts the newly installed filesystem at /mnt/sysimage. You are operating inside the target system, not the installation media.
%post
# This block executes inside the newly installed system
# Package manager caches are already populated from the installation media
dnf install -y vim-enhanced curl
# Installs additional tools that were not in the base package groups
systemctl enable sshd
# Ensures the SSH daemon starts on boot without manual intervention
restorecon -Rv /etc/ssh
# Resets SELinux contexts for any files modified during post-install
%end
SELinux runs in enforcing mode by default. Any file created or modified in %post inherits the wrong context until you run restorecon. Skipping this step causes permission denied errors on the first boot. Run restorecon -Rv /path/to/dir after every file operation in the post script. Config files in /etc/ are user-modified. Files in /usr/lib/ ship with the package. Edit /etc/. Never edit /usr/lib/. The same rule applies to your post-install scripts.
Validate the configuration before you deploy it. The anaconda package ships with ksvalidator, which checks syntax and catches missing directives.
dnf install anaconda
# Provides the ksvalidator tool for syntax checking
ksvalidator /path/to/ks.cfg
# Parses the file and exits with code 0 if the syntax is valid
If ksvalidator returns an error, fix the line it points to. Anaconda will abort the installation on the first syntax error it encounters. Do not ignore validation warnings.
Booting with the kickstart file
Anaconda needs to find the ks.cfg file before it loads the graphical installer. You can serve it over HTTP, place it on a USB drive, or embed it in a custom ISO. The boot parameter tells Anaconda where to look.
Boot the machine from the Fedora installation media. When the GRUB menu appears, highlight the installation entry and press e. This drops you into the kernel parameter editor.
Locate the line starting with linux. Append the kickstart path to the end of that line. Use the correct URI scheme for your delivery method.
linux ... inst.ks=http://192.168.1.100/ks.cfg quiet
# HTTP delivery requires a reachable web server hosting the file
# The quiet flag suppresses installer console spam during execution
If the file lives on a USB stick formatted as FAT32, reference it directly. The installer mounts removable media automatically and exposes it under /run/install/.
linux ... inst.ks=file:///run/install/ks.cfg quiet
# File URI scheme tells Anaconda to read from the mounted USB volume
# Ensure the USB is plugged in before booting the installation media
Press Ctrl+X or F10 to boot. Anaconda parses the kernel line, fetches the kickstart file, and begins the installation. The screen will show progress bars for partitioning, package installation, and bootloader configuration. No prompts will appear.
Watch the console output during the first few minutes. If Anaconda cannot reach the HTTP server or cannot read the USB drive, it falls back to the interactive installer. You will see a message asking you to select a language. That means the inst.ks parameter failed. Check your network configuration or USB mount point. Fedora's release cadence is six months. The N-2 release goes EOL when N+1 ships. Plan your repository URLs and ISO downloads on that cycle.
Verifying the installation
After the system reboots, log in as root. Check the installation logs to confirm every step completed successfully. Anaconda writes detailed logs to /root/anaconda-* and /var/log/anaconda-*.
journalctl -xeu anaconda
# Shows installer service logs with explanatory context
# The x flag adds troubleshooting hints to standard journal output
grep -i "kickstart" /var/log/anaconda.log
# Confirms the installer actually loaded and parsed your configuration file
Verify the partition layout matches your directives.
lsblk -f
# Displays block devices, filesystem types, and mount points
# Compare the output against your clearpart and part directives
systemctl status firewalld
# Confirms the firewall service is active and loaded
# Your ks.cfg directive should have enabled it automatically
Check that your %post commands ran. If you installed packages or enabled services, they should be present and active. If something is missing, the post script likely failed silently. Check /root/anaconda-post.log for the exact error. Run journalctl -xe first. Read the actual error before guessing.
Common pitfalls and error patterns
The installer will not guess your intentions. A missing directive or a typo in a package name stops the process or produces a broken system.
You will see Error: Package group @^minimal-environment not found if you are using a netinstall ISO that does not include the group metadata. Netinstall images only contain the base environment. Use a full DVD ISO or point Anaconda to a repository that contains the group data.
You will see Kickstart error: Invalid option 'device=eth0' if the network interface name changed between your test machine and the target hardware. Modern Fedora systems use predictable network interface names like enp3s0 or ens18. Use --device=link to let Anaconda pick the first available interface, or specify the exact name your hardware uses.
You will see SELinux policy prevents access if your %post script writes to /etc/ or /usr/ without resetting contexts. The installer creates files with install_t context. Services like sshd or nginx will refuse to read configuration files with the wrong label. Always run restorecon after modifying system directories. SELinux denials show up in journalctl -t setroubleshoot with a one-line summary. Read those before disabling SELinux.
You will see the installer hang at Starting Anaconda if the HTTP server hosting ks.cfg requires authentication or returns a redirect. Anaconda does not handle HTTP 301 redirects gracefully in all versions. Serve the file directly without redirects and without basic auth.
When to use Kickstart versus other methods
Use Kickstart when you need to deploy Fedora on bare metal or virtual machines with full control over partitioning and package selection. Use Anaconda's interactive installer when you are setting up a single workstation and want to review every choice before committing. Use dnf system-upgrade when you are migrating an existing system to a newer Fedora release. Use rpm-ostree when you are deploying immutable Fedora Silverblue systems that require atomic updates and rollback capabilities. Use cloud-init when you are provisioning Fedora images on AWS, GCP, or Azure where the base image is already baked and you only need user data configuration.
Where to go next
- How to Automate Tasks with Bash Scripts on Fedora
- How to Configure CPU Frequency Scaling and Governors on Fedora
- How to Monitor Disk I/O Performance on Fedora (iotop, iostat)
Run ksvalidator before every deployment. A syntax error on paper is a wasted hour on hardware.