PXE and Kickstart: Unattended Rocky Linux 9 Installation with UEFI
EdwardMoon
This guide covers UEFI x86_64 clients that obtain signed shim/GRUB and the kernel through PXE/TFTP, then read the Rocky Linux installation tree and Kickstart file over HTTP. UEFI HTTP Boot and legacy BIOS require different boot files and DHCP rules and are outside this example's scope.
The example uses Rocky Linux 9.8. The installation media's kernel, initrd, and package tree must all belong to the same release. DHCP affects the entire network, so test first on an isolated VLAN that does not conflict with production DHCP.
PXE and Kickstart Components
| Component | Role | Operational rule |
|---|---|---|
| DHCP | Supplies IP configuration, boot filename, and boot server | Do not compete with an existing authoritative DHCP server |
| TFTP/PXE | Transfers UEFI shim, GRUB, kernel, and initrd | Serve only the small boot files |
| HTTP | Installation tree and ks.cfg | Access logging and minimal exposure |
| Kickstart | Automates Anaconda installation responses | Validation, version control, and separate secrets |
Step 1: Verify the ISO and Prepare the Installation Tree
# First download the official Rocky 9.8 x86_64 DVD ISO into the current directory.
curl --fail --location --remote-name https://download.rockylinux.org/pub/rocky/9.8/isos/x86_64/CHECKSUM
test -f Rocky-9.8-x86_64-dvd.iso
sha256sum --check --ignore-missing CHECKSUM
# Continue only after the check above reports OK for that exact DVD ISO.
sudo dnf install -y httpd rsync dnsmasq tcpdump
sudo install -d -m 0755 /mnt/rocky9 /var/www/html/rocky/9.8
sudo mount -o loop,ro Rocky-9.8-x86_64-dvd.iso /mnt/rocky9
# The target below is a dedicated new 9.8 installation tree; --delete can remove existing files.
sudo rsync -aH --delete /mnt/rocky9/ /var/www/html/rocky/9.8/
sudo restorecon -RFv /var/www/html/rocky
Confirm that the CHECKSUM file from the official HTTPS source matches the exact 9.8 DVD ISO. An OK result for another file does not verify this ISO. If organizational supply-chain policy requires signatures, also verify Rocky's signature using a trusted official key. Keep the ISO mounted read-only and preserve the complete installation tree.
Step 2: Configure HTTP
Assign 192.0.2.10/24 to the dedicated lab NIC ens192 beforehand, and place that connection in firewalld's internal zone. Adapt the gateway, DNS, and NIC name to the isolated VLAN. Before enabling the services below, verify that firewall-cmd --get-zone-of-interface=ens192 returns internal. Do not move a production NIC into this lab zone.
sudo dnf install -y httpd
sudo systemctl enable --now httpd
sudo firewall-cmd --permanent --zone=internal --add-service=http
sudo firewall-cmd --reload
curl --fail --head http://192.0.2.10/rocky/9.8/.treeinfo
sudo journalctl -u httpd --since '-10 min' --no-pager
Step 3: Install TFTP Boot Files
Use signed UEFI files from the same verified DVD ISO. shim loads grubx64.efi from the same directory, and GRUB loads the matching release's kernel and initrd. Do not start a separate tftp.socket; dnsmasq in the next step handles UDP 69.
sudo install -d -m 0755 /var/lib/tftpboot/rocky9
sudo install -m 0644 /mnt/rocky9/EFI/BOOT/BOOTX64.EFI /var/lib/tftpboot/shimx64.efi
sudo install -m 0644 /mnt/rocky9/EFI/BOOT/grubx64.efi /var/lib/tftpboot/grubx64.efi
sudo install -m 0644 /mnt/rocky9/images/pxeboot/vmlinuz /var/lib/tftpboot/rocky9/vmlinuz
sudo install -m 0644 /mnt/rocky9/images/pxeboot/initrd.img /var/lib/tftpboot/rocky9/initrd.img
sudo restorecon -RFv /var/lib/tftpboot
ls -l /var/lib/tftpboot/{shimx64.efi,grubx64.efi,rocky9/vmlinuz,rocky9/initrd.img}
If the paths are absent or filenames differ on the media, stop copying and inspect the ISO architecture and contents. With Secure Boot, the shim, GRUB, and kernel signature chain must align with firmware trust settings.
Step 4: Configure DHCP and TFTP
Save the following to /etc/dnsmasq.d/pxe.conf and check for conflicts with existing dnsmasq configuration. Treat DHCP architecture values 7 and 9 as UEFI x86_64. Do not advertise boot files to other architectures.
sudoedit /etc/dnsmasq.d/pxe.conf
This dnsmasq configuration is for a dedicated lab VLAN. If production DHCP already exists, do not apply it; have the existing DHCP administrator configure architecture-specific bootfile and next-server options.
port=0
interface=ens192
bind-interfaces
dhcp-range=192.0.2.100,192.0.2.180,255.255.255.0,12h
dhcp-option=3,192.0.2.1
dhcp-option=6,192.0.2.53
enable-tftp
tftp-root=/var/lib/tftpboot
dhcp-match=set:efi64,option:client-arch,7
dhcp-match=set:efi64,option:client-arch,9
dhcp-boot=tag:efi64,shimx64.efi
log-dhcp
sudo dnsmasq --test
sudo systemctl enable --now dnsmasq
sudo firewall-cmd --permanent --zone=internal --add-service=dhcp
sudo firewall-cmd --permanent --zone=internal --add-service=tftp
sudo firewall-cmd --reload
sudo journalctl -u dnsmasq -f
Step 5: Create the Boot Menu
UEFI GRUB Menu
Save this menu as /var/lib/tftpboot/grub.cfg in the TFTP root. During testing, check dnsmasq logs or packet captures for the actual path requested by GRUB.
sudoedit /var/lib/tftpboot/grub.cfg
sudo restorecon -RFv /var/lib/tftpboot
set timeout=5
menuentry 'Install Rocky Linux 9.8 with Kickstart' {
linuxefi /rocky9/vmlinuz ip=dhcp inst.repo=http://192.0.2.10/rocky/9.8/ inst.ks=http://192.0.2.10/ks/rocky9.cfg
initrdefi /rocky9/initrd.img
}
Verify GRUB commands against the installation media's real boot configuration. Some firmware/GRUB combinations use linux and initrd commands, so test booting on a VM of the same type first.
Step 6: Write a Least-Privilege Kickstart File
Save the example to /var/www/html/ks/rocky9.cfg. Replace the opsadmin password hash with a real hash and restrict HTTP access to the installation VLAN. Leaving the placeholder unchanged will prevent a normal administrator login.
sudo install -d -m 0755 /var/www/html/ks
sudoedit /var/www/html/ks/rocky9.cfg
text
url --url=http://192.0.2.10/rocky/9.8/
lang ko_KR.UTF-8
keyboard kr
timezone Asia/Seoul --utc
network --bootproto=dhcp --device=link --activate
rootpw --lock
user --name=opsadmin --groups=wheel --iscrypted --password='$6$REPLACE_WITH_A_REAL_HASH'
ignoredisk --only-use=sda
zerombr
clearpart --all --initlabel --drives=sda
autopart --type=lvm
selinux --enforcing
firewall --enabled --service=ssh
reboot
%packages
@^minimal-environment
chrony
%end
%post --log=/root/ks-post.log
systemctl enable chronyd
%end
clearpart --all erases data on the selected disks. Verify disk names and WWNs for each server model and test on an empty VM first. Replace the password placeholder with a real strong hash, while keeping secrets off public web servers and Git repositories.
Step 7: Validate Kickstart Syntax
sudo dnf install -y pykickstart
ksvalidator -v RHEL9 /var/www/html/ks/rocky9.cfg
curl --fail http://192.0.2.10/ks/rocky9.cfg | head
curl --fail http://192.0.2.10/rocky/9.8/.treeinfo | head
sudo restorecon -RFv /var/www/html/ks
Step 8: Test Installation on a UEFI VM
- Create a UEFI VM with an empty disk on the dedicated VLAN.
- Capture packets to verify the DHCP offer's address, next-server, and bootfile.
- Verify the inst.repo and inst.ks locations specified in the kernel arguments respond correctly.
- Watch Anaconda logs and HTTP access logs together during installation.
- After reboot, verify SELinux, the firewall, time synchronization, and the management account.
sudo tcpdump -ni ens192 -vvv 'port 67 or port 68 or port 69'
sudo tail -f /var/log/httpd/access_log /var/log/httpd/error_log
# Check from the installation environment's shell
ip address
cat /proc/cmdline
curl --fail http://192.0.2.10/ks/rocky9.cfg
journalctl -b --no-pager | tail -200
Step 9: Verify the Installed System
cat /etc/rocky-release
uname -r
sudo dnf upgrade --refresh -y
sudo systemctl --failed --no-pager
getenforce
sudo firewall-cmd --list-all
chronyc tracking
sudo journalctl -p err -b --no-pager
Treat the completed installation as a reproducible build artifact. Preserve the ISO checksum, Kickstart version, boot-file hashes, tested firmware type, and successful installation records together.
Security and Operational Considerations
- Restrict the Kickstart URL to the required installation VLAN.
- Do not embed registration tokens or SSH private keys in the file.
- Monitor HTTP access logs for unusual request volumes.
- Use TFTP only for boot files and serve installation packages over HTTP.
- Review production DHCP changes and prepare rollback plans.
- Block PXE access outside active installation windows.
Official Resources and Related Articles
- RHEL 9 automated installation documentation
- Preparing RHEL 9 UEFI HTTP installation
- Starting RHEL 9 Kickstart installations
- Rocky Linux 9.8 Installation Guide
- Ansible Automation Operations Guide
Conclusion
PXE and Kickstart combine DHCP, boot-file delivery, an HTTP installation tree, and validated installation instructions into one workflow. For this UEFI x86_64 example, verify the PXE/TFTP and HTTP paths, then pass ISO checksum validation, syntax checks, empty-VM installation, and post-installation security checks before using unattended installation in production.