Installing an operating system on each server manually consumes too much time and can cause errors. PXE (Preboot Execution Environment) with Kickstart automation allows your servers automatically download the OS from a central server over the network, configure themselves, and reboot. In this guide, you will learn Automated OS Setup with PXE and Kickstart.
When you combine PXE boot with Kickstart scripts, there is no need for USB drives, no clicking through installers, and no manual steps.
This guide will show you how to set up a complete PXE deployment server on RHEL 9, configure DHCP, TFTP, and HTTP servers, create a Kickstart automation file, and deploy multiple machines in minutes.
Whether you're managing a small cluster or a large cluster at PerLod Hosting, this technique is essential for scaling your infrastructure efficiently.
Prerequisites for Automated OS Setup with PXE and Kickstart
In this article, you will learn to set up a central deployment server that allows other servers in the same network to boot from the network and automatically install the OS.
Here is what you need for this guide:
- Server: A flexible dedicated server or VM with a static IP address like 192.168.100.10 running AlmaLinux 9 or any RHEL 9, like Rocky Linux 9.
- Client: A bare-metal server or VM connected to the same network, configured to boot from Network (PXE) in BIOS/UEFI.
- ISO File: The AlmaLinux 9 minimal ISO.
Install Required Services for PXE Kickstart Baremetal Provisioning
A PXE boot server requires three core services working together, including:
- DHCP assigns IP addresses to clients requesting a network boot.
- TFTP transfers the bootloader (firmware files) to the booting machine.
- HTTP hosts the OS installation files and Kickstart automation script.
As a root user, run the following commands to update the system packages and install these required services:
dnf update -ydnf install dhcp-server tftp-server httpd syslinux -y
Enable services to start on boot:
systemctl enable --now dhcpd tftp.socket httpd
Open Firewall Ports and Adjust SELinux in PXE Server
Before PXE clients can access the services, the PXE server must allow the required network traffic. You must open the firewall for DHCP, TFTP, and HTTP so clients can get an IP address, download the boot files, and access the installation source.
Also, SELinux can block TFTP/HTTP access if the correct labels aren’t set. You can temporarily set SELinux to Permissive mode for this guide.
In production, it's better to keep Enforcing and applying proper SELinux rules.
Allow traffic for DHCP (67/UDP), TFTP (69/UDP), and HTTP (80/TCP) by using the commands below:
firewall-cmd --permanent --add-service=dhcpfirewall-cmd --permanent --add-service=tftpfirewall-cmd --permanent --add-service=http
Reload the firewall to apply the changes:
Set SELinux to Permissive mode to prevent permission issues during setup with the commands below:
setenforce 0sed -i 's/^SELINUX=.*/SELINUX=permissive/g' /etc/selinux/config
HTTP Install Source for PXE and Kickstart
Now that DHCP and TFTP can boot the client, the installer still needs a place to download the OS packages and read the Kickstart file from. You can turn your PXE server into a simple HTTP repo by mounting the AlmaLinux ISO and copying its contents into Apache’s web directory.
If you don't download the ISO image, you can use the commands below to download it:
cd /tmpwget https://repo.almalinux.org/almalinux/9/isos/x86_64/AlmaLinux-9-latest-x86_64-minimal.iso
Create a directory for the OS files by using the command below:
mkdir -p /var/www/html/almalinux9
Mount the ISO with the following command:
mount -o loop,ro /tmp/AlmaLinux-9-latest-x86_64-minimal.iso /mnt
cp -r /mnt/* /var/www/html/almalinux9/
Then, restore permissions so Apache can read them:
chmod -R 755 /var/www/html/almalinux9umount /mnt
Now every PXE client can pull the full installer content directly from:
http://PXE-SERVER-IP/almalinux9/
This makes deployments fast, consistent, and easy to scale.
Configure UEFI PXE Boot Files
Most modern dedicated servers boot in UEFI mode, so your PXE setup must provide the correct UEFI boot chain over TFTP.
You can copy the signed UEFI bootloader files, including Shim and GRUB, with the installer kernel (vmlinuz) and initramfs (initrd.img) into your TFTP root so the client can boot the installer and then continue the automated Kickstart install.
Create the UEFI-specific directory with the command below:
mkdir -p /var/lib/tftpboot/uefi
Copy the bootloaders from the ISO content we just extracted with the commands below:
cp /var/www/html/almalinux9/EFI/BOOT/BOOTX64.EFI /var/lib/tftpboot/uefi/cp /var/www/html/almalinux9/EFI/BOOT/grubx64.efi /var/lib/tftpboot/uefi/
Copy the Kernel (vmlinuz) and Initial Ram Disk (initrd) with the following commands:
cp /var/www/html/almalinux9/images/pxeboot/vmlinuz /var/lib/tftpboot/uefi/cp /var/www/html/almalinux9/images/pxeboot/initrd.img /var/lib/tftpboot/uefi/
Then, set the correct permissions:
chmod -R 755 /var/lib/tftpboot/
Note: The UEFI firmware typically downloads BOOTX64.EFI, which then loads grubx64.efi, and GRUB reads a grub.cfg file (commonly placed alongside them under a uefi/ directory).
Configure DHCP for PXE Boot
DHCP is what makes PXE discoverable. When a client boots from the network, it first asks for an IP address, and the DHCP server replies with both an IP lease and the information needed to start the boot process (the PXE server address and the boot file name).
At this point, you can configure DHCP to point clients to your PXE server and to deliver the correct UEFI bootloader by detecting the client's architecture type.
This ensures modern UEFI servers boot correctly, while still allowing an optional legacy BIOS fallback if you need it.
Edit /etc/dhcp/dhcpd.conf file as shown below:
option space pxelinux;option pxelinux.magic code 208 = string;option pxelinux.configfile code 209 = text;option pxelinux.pathprefix code 210 = text;option pxelinux.reboottime code 211 = unsigned integer 32;option architecture-type code 93 = unsigned integer 16; subnet 192.168.100.0 netmask 255.255.255.0 { range 192.168.100.100 192.168.100.200; option routers 192.168.100.1; option subnet-mask 255.255.255.0; option domain-name-servers 8.8.8.8; next-server 192.168.100.10; if option architecture-type = 00:07 { filename "uefi/BOOTX64.EFI"; } else if option architecture-type = 00:09 { filename "uefi/BOOTX64.EFI"; } else { filename "pxelinux.0"; }}
Once you're done, restart DHCP with the command below:
Create a Kickstart File for Unattended Installation
Kickstart is the automation part of your PXE deployment, which pre-answers the installer questions such as language, disk layout, network, packages, users, and post-install actions, so the installation runs fully unattended.
In this step, you can generate a secure hashed root password, write your ks.cfg, and host it over HTTP so every PXE-booted server can fetch it during installation.
Generate an encrypted root password with the command below:
openssl passwd -6 MySecurePassword123
Create /var/www/html/ks.cfg file and add the following script to the file:
textfirstboot --disablekeyboard --vckeymap=us --xlayouts='us'lang en_US.UTF-8 network --bootproto=dhcp --device=link --ipv6=auto --activatenetwork --hostname=localhost.localdomain rootpw --iscrypted $6$randomsalt$hashedpasswordstring... timezone Asia/Dubai --isUtc url --url="http://192.168.100.10/almalinux9/" bootloader --location=mbr --append="rhgb quiet crashkernel=auto" zerombrclearpart --all --initlabel autopart --type=lvm reboot %packages@^minimal-environmentkexec-toolsvimcurl%end %postecho "Welcome to your new server" > /etc/motd%end
Once you are done, you can check for any syntax errors by installing the pykickstart tool and running the command below:
ksvalidator /var/www/html/ks.cfg
After the client downloads the UEFI boot files over TFTP, GRUB takes control and needs a boot menu that tells the installer kernel where to find your installation source and your Kickstart file.
At this point, you can create a Grub file with two choices, including an unattended install and a manual install for interactive setups.
Create /var/lib/tftpboot/uefi/grub.cfg file and add the following content to the file:
set default="0"set timeout=10 menuentry 'Install AlmaLinux 9 (Automated)' { linuxefi uefi/vmlinuz ip=dhcp inst.ks=http://192.168.100.10/ks.cfg inst.repo=http://192.168.100.10/almalinux9/ initrdefi uefi/initrd.img} menuentry 'Install AlmaLinux 9 (Manual)' { linuxefi uefi/vmlinuz ip=dhcp inst.repo=http://192.168.100.10/almalinux9/ initrdefi uefi/initrd.img}
Key points:
- linuxefi: Loads the kernel.
- ip=dhcp: Tells the kernel to request an IP during boot.
- inst.ks=…: Points to the automation script.
- inst.repo=…: Points to the installation files.
Test PXE with Kickstart Deployment
Now that everything is configured, you can restart services, do a quick permissions sanity check, and then PXE-boot a client to confirm the deployment works.
Restart the services with the commands below:
systemctl restart dhcpd httpd
Ensure /var/lib/tftpboot and /var/www/html are readable.
Boot the Client:
- Turn on the dedicated server or VM.
- Enter BIOS or Boot Menu.
- Select PXE and Network Boot.
During boot:
- You should see the client get an IP (DHCP).
- It will download BOOTX64.EFI (TFTP).
- The GRUB menu will appear.
- Once the timer expires or you press Enter, it will load the kernel, fetch the ks.cfg file from HTTP, and automatically start formatting the drive.
Troubleshooting Common PXE Issues
Here are the most common PXE errors and their solutions:
Issue1. Client times out / skips PXE:
If the client doesn’t get an IP fast enough, it usually falls back to the next boot device. Start by confirming the DHCP service is running and actually listening on the correct interface, especially if the PXE server has multiple NICs or VLANs.
- Check DHCP status and logs with the commands below:
systemctl status dhcpdjournalctl -u dhcpd
- Verify the client’s MAC is not blocked upstream.
- Confirm the DHCP scope matches the client subnet and that the server’s firewall allows DHCP.
Issue 2. TFTP “File not found”:
This almost always means DHCP is pointing to a file that doesn’t exist under the TFTP root, or GRUB is referencing paths incorrectly.
Check TFTP logs with the command below:
Ensure the boot filename matches your folder layout. For example, uefi/BOOTX64.EFI must exist under /var/lib/tftpboot/uefi/.
In grub.cfg, use paths relative to the TFTP root, for example:
Correct: uefi/vmlinuzWrong: /var/lib/tftpboot/uefi/vmlinuz
Confirm permissions allow reads with the command below:
chmod -R 755 /var/lib/tftpboot
Issue 3. Dracut initqueue timeout (installer can’t reach repo/ks):
When Dracut hangs with an initqueue timeout, the kernel booted successfully, but the installer can’t fetch "inst.repo" or "inst.ks" over the network.
Verify the URLs are correct and reachable from the client network:
http://192.168.100.10/almalinux9/ http://192.168.100.10/ks.cfg
Check Apache access logs while booting a client:
tail -f /var/log/httpd/access_log
If you can open a shell in the installer environment, test connectivity to the PXE server IP and HTTP URLs.
Confirm firewall allows HTTP and DHCP, and that routing is correct for that subnet.
Conclusion
At this point, you have a fully automated bare-metal server deployment pipeline, which is one of the most powerful tools in any IT infrastructure. PXE with Kickstart setup handles DHCP, network boot, OS installation, partitioning, package selection, and post-install configuration without a single manual click.
- DHCP discovers and directs network clients to your PXE server.
- TFTP transfers the UEFI bootloader chain.
- HTTP delivers the OS installer packages and automation scripts.
- Kickstart removes human interaction entirely.
We hope you enjoy this guide on Automated OS Setup with PXE and Kickstart. Subscribe to our X and Facebook channels to get the latest articles.