Configure a PXE Boot Server with Kickstart on CentOS 8 – Easy guide


PXE Boot Server (Preboot Execution Environment) or we can call it as Network Boot installation.

Let’s walk through a simple guide to make our life easier while installing and configuring a huge number of Linux servers. It can be physical or virtual, all the installation process can be automated by hitting a single key (F12, F11 or F10) from your keyboard.

To have a working PXE environment first we should need a DHCP, TFTP, FTP or HTTP server. In our guide, we will stick with HTTP. Once our PXE Boot Server is ready it will push the boot loader files into Linux clients by assigning random IP or a static IP, Then the kickstart file will be read by the installer and remaining installation process will be automated. Thus the PXE will help us to perform an unattended Operating System installation on massive numbers of Virtual or Physical servers.

The below guide will work on RHEL 8, CentOS 8 and Oracle Linux 8 as well.

Our Server Setup

The server specification we are using is

1 vCPU
2 GB minimum Memory
40 GB minimum disk space
1 Interface (NIC)

The base operating System we are using in our PXE boot server is CentOS 8.2 Linux.

[root@pxe ~]# cat /etc/redhat-release 
CentOS Linux release 8.2.2004 (Core) 
[root@pxe ~]#

The IP address used for PXE Boot Server is resolving to pxe.linuxsysadmins.local

Publishing Repo Content

This is a separate filesystem created on top of a logical volume. If you are looking to set up one using Logical volume have a look into LVM guide.

[root@pxe ~]# df -hP /webdata/
Filesystem                         Size  Used Avail Use% Mounted on
/dev/mapper/vg_webdata-lv_webdata   40G  2.0G   38G   5% /webdata
[root@pxe ~]#

If you don’t need a LVM setup just create a directory to copy our Installation media files.

# mkdir /webdata

Restore the SELinux context for newly created directory.

[root@pxe ~]# restorecon -Rv /webdata/
Relabeled /webdata from system_u:object_r:unlabeled_t:s0 to system_u:object_r:default_t:s0
[root@pxe ~]# 

Set the web server context for the newly created directory

# semanage fcontext -a -t httpd_sys_content_t  "/webdata(/.*)?"

Once again restore the context to verify whether the Web server context in place.

[root@pxe ~]# restorecon -Rv /webdata/
Relabeled /webdata from system_u:object_r:default_t:s0 to system_u:object_r:httpd_sys_content_t:s0
[root@pxe ~]# 

Install Apache Web Server

Install apache web server to distribute the installation files.

# dnf install httpd -y

We should get similar to below at the end of the installation.


[root@pxe ~]#

Enable the service to start persistently.

# systemctl enable httpd

Allow http traffic by running firewalld command.

# firewall-cmd --add-service=http --permanent 
# firewall-cmd --reload 

Make sure to reload the firewalld.

Configure Apache Web Server

Create a custom apache Virtual Host entry for installation content.

Disable the Apache Welcome Page.

# mv /etc/httpd/conf.d/welcome.conf /etc/httpd/conf.d/welcome.conf_backup

By following, copy the sample virtual host entry and do the required changes.

# cp /usr/share/doc/httpd/httpd-vhosts.conf /etc/httpd/conf.d/pxe.conf

Edit with anyone of your favorite editor.

# vim /etc/httpd/conf.d/pxe.conf 

The finalized virtual host entry for our setup is below.

<VirtualHost *:80>
    ServerAdmin admin@linuxsysadmins.local
    DocumentRoot /webdata/
    ServerName pxe.linuxsysadmins.local
    ServerAlias pxe
    ErrorLog "/var/log/httpd/pxe-error_log"
    CustomLog "/var/log/httpd/pxe-access_log" common

<Directory /webdata/>
    Options +Indexes
    AllowOverride None
    Require all granted

Finally, Restart the apache service.

# systemctl restart httpd
# systemctl status httpd

Copying Installation Media

In my case, My PXE server setup in on a virtual machine, so I have a CentOS 8.2 ISO file attached to the PXE Virtual machine.

# mount /dev/sr0 /mnt

In future, I have an idea to add more OS in PXE boot, so let me create a separate directory for CentOS 8 and copy the content to it.

# mkdir /webdata/centos8
# rsync -avz --progress /mnt/* /webdata/centos8/

Now you should able to browse the files from anyone of web browser.

Create a KickStart file

To avoid manual intervention while performing the installation, we have come up with the unattended method of installation. To do so, we are about to use the kickstart file. It will automate the installation process by reading the kickstart file.

Let’s create a separate directory to store our kick start file under /webdata`

# mkdir /webdata/ks_files

Create the kickstart file or use the existing one from any server, it can be found under the /root. I have copied an existing kickstart file from the PXE server and modified to match my requirement.

# cat /root/anaconda-ks.cfg > /webdata/ks_files/centos8-ks.cfg
ignoredisk --only-use=sda
autopart --type=lvm
# Partition clearing information
clearpart --all --initlabel
# Use graphical install
# Use Network installation method
url --url="http://pxe.linuxsysadmins.local/centos8/BaseOS"
# Keyboard layouts
keyboard --vckeymap=us --xlayouts='us'
# System language
lang en_US.UTF-8
# Network information
network  --bootproto=dhcp
network  --hostname=localhost.localdomain
# Root password
rootpw --iscrypted $6$x2KEcFpCRhtXlXeL$fc.Q8qqD7rLNiF.ocm/esbZAIXlevNujQskP4hzcPhtL0
# Run the Setup Agent on first boot
firstboot --enable
# Do not configure the X Window System
# System services
services --disabled="chronyd"
# System timezone
timezone Asia/Dubai --isUtc --nontp
user --groups=wheel --name=sysadmins --password=$6$WS5ANJJ57zt/ --iscrypted --gecos="sysadmins"



%addon com_redhat_kdump --enable --reserve-mb='160M'


pwpolicy root --minlen=6 --minquality=1 --notstrict --nochanges --notempty
pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok
pwpolicy luks --minlen=6 --minquality=1 --notstrict --nochanges --notempty

Now try to access the kickstart file from anyone of the web browser to make sure you are able to read the file.

PXE Web Content
PXE Web Content

Get into the ks_file directory and click the centos8-ks.cfg file. If you get any forbidden error or permission issue then you might done some misconfiguration in your apache Virtual host entry.

pxe boot kickstart
pxe boot kickstart

We have done with the kickstart file part.

Install DHCP Server

Let’s start with installing and configuring DHCP server.

# dnf install dhcp-server -y


[root@pxe ~]#

Exclude DHCP traffic by allowing through firewall.

# firewall-cmd --add-service=dhcp --permanent 
# firewall-cmd --reload 

Configure DHCP Server

Configure DHCP for PXE Boot Server, The sample config files can be found under /usr/share/doc/dhcp-server/ copy the sample file and make the necessary changes.

# cp /usr/share/doc/dhcp-server/dhcpd.conf.example /etc/dhcp/dhcpd.conf

Use anyone of your favorite editor

# vi /etc/dhcp/dhcpd.conf

Change the values for your setup. You need to have a Valid DNS server, if you don’t have an existing DNS follow this guide to setup one.

Setup an Identity Management server in Linux using IPA

I have few of Physical nodes in my home lab, so let me put those MAC address to get the static IP for them. Remaining virtual machines will get a random IP from the defined DHCP range.

# dhcpd.conf

# option definitions common to all supported networks...
option domain-name "linuxsysadmins.local";
option domain-name-servers idm1.linuxsysadmins.local, idm2.linuxsysadmins.local;

default-lease-time 600;
max-lease-time 7200;

# If this DHCP server is the official DHCP server for the local
# network, the authoritative directive should be uncommented.

# Use this to send dhcp log messages to a different log file (you also
# have to hack syslog.conf to complete the redirection).
log-facility local7;

# PXE configs
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;

# This declaration allows BOOTP clients to get dynamic addresses,

subnet netmask {
    range dynamic-bootp;
    option broadcast-address;
    option routers;
# Static IP for clients
host pve2 {
  option host-name "pve2.linuxsysadmins.local";
  hardware ethernet 1C:55:6A:60:12:57;
  filename "pxelinux.0";

host pve3 {
  option host-name "pve3.linuxsysadmins.local";
  hardware ethernet 1C:49:6A:60:6H:75;
  filename "pxelinux.0";

host pve4 {
  option host-name "pve4.linuxsysadmins.local";
  hardware ethernet 1C:49:7H:60:R4:9B;
  filename "pxelinux.0";

# You can declare a class of clients and then do address allocation
# based on that.   The example below shows a case where all clients
# in a certain class get addresses on the 10.17.224/24 subnet, and all
# other clients get addresses on the 10.0.29/24 subnet.

        class "pxeclients" {
            match if substring (option vendor-class-identifier, 0, 9) = "PXEClient";
            # PXE server hostname or IP
            next-server pxe.linuxsysadmins.local;
            if option architecture-type = 00:07 {
                filename "BOOTX64.EFI";
            else {
                filename "pxelinux.0";
#### ----End of Config---- ####

Once we have a DNS and DHCP the IP assignment will be handled by DHCP and the hostname will be resolved from the DNS server.

Enable the DHCPD service persistently to start during the reboot, then start the service.

# systemctl enable dhcpd
# systemctl start dhcpd

We have done with the DHCP part.

Install TFTP Server

Let’s start to install the required packages for TFTP server setup.

# dnf install tftp-server -y
Running transaction
  Preparing        :                                                1/1 
  Installing       : tftp-server-5.2-24.el8.x86_64                  1/1 
  Running scriptlet: tftp-server-5.2-24.el8.x86_64                  1/1 
  Verifying        : tftp-server-5.2-24.el8.x86_64                  1/1 


[root@pxe ~]#

Right after installing the TFTP we need to start with configuring PXE boot.

Configure PXE

The first steps is to install the syslinux package, this is a suite of bootloaders.

# dnf install syslinux -y
Running transaction
  Preparing        :                                                1/1 
  Installing       : mtools-4.0.18-14.el8.x86_64                    1/3 
  Running scriptlet: mtools-4.0.18-14.el8.x86_64                    1/3 
  Installing       : syslinux-nonlinux-6.04-4.el8.noarch            2/3 
  Installing       : syslinux-6.04-4.el8.x86_64                     3/3 
  Running scriptlet: syslinux-6.04-4.el8.x86_64                     3/3 
  Verifying        : mtools-4.0.18-14.el8.x86_64                    1/3 
  Verifying        : syslinux-6.04-4.el8.x86_64                     2/3 
  Verifying        : syslinux-nonlinux-6.04-4.el8.noarch            3/3 


[root@pxe ~]#

Once we install the syslinux package we will get the boot image file pxelinux.0, copy it under the /var/lib/tftpboot

# cp /usr/share/syslinux/pxelinux.0 /var/lib/tftpboot/

Create a separate directory for CentOS 8 under the tftp home directory

# mkdir /var/lib/tftpboot/centos8

Copy the required file from mounted ISO file to newly created centos8 directory

# cp -v /mnt/images/pxeboot/{vmlinuz,initrd.img} /var/lib/tftpboot/centos8/
[root@pxe ~]# ls -lthr /var/lib/tftpboot/centos8/
total 71M
-r-xr-xr-x. 1 root root 8.6M Nov 20 10:14 vmlinuz
-r--r--r--. 1 root root  63M Nov 20 10:14 initrd.img
[root@pxe ~]#

Copy the menu files generated by syslinux package to tftpboot home directory

# cp -v /usr/share/syslinux/{menu.c32,vesamenu.c32,ldlinux.c32,libcom32.c32,libutil.c32} /var/lib/tftpboot/
[root@pxe ~]# cp -v /usr/share/syslinux/{menu.c32,vesamenu.c32,ldlinux.c32,libcom32.c32,libutil.c32} /var/lib/tftpboot/
'/usr/share/syslinux/menu.c32' -> '/var/lib/tftpboot/menu.c32'
'/usr/share/syslinux/vesamenu.c32' -> '/var/lib/tftpboot/vesamenu.c32'
'/usr/share/syslinux/ldlinux.c32' -> '/var/lib/tftpboot/ldlinux.c32'
'/usr/share/syslinux/libcom32.c32' -> '/var/lib/tftpboot/libcom32.c32'
'/usr/share/syslinux/libutil.c32' -> '/var/lib/tftpboot/libutil.c32'
[root@pxe ~]# 

Create the boot menu

# mkdir /var/lib/tftpboot/pxelinux.cfg
# vim /var/lib/tftpboot/pxelinux.cfg/default

Append menu content to /var/lib/tftpboot/pxelinux.cfg/default file.

default vesamenu.c32
prompt 1
timeout 60

display boot.msg

label linux
  menu label ^Install CentOS 8 in Graphical Mode
  menu default
  kernel centos8/vmlinuz
  append initrd=centos8/initrd.img ip=dhcp inst.repo=http://pxe.linuxsysadmins.local/centos8 ks=http://pxe.linuxsysadmins.local/ks_files/centos8-ks.cfg
label vesa
  menu label Install CentOS 8 with ^basic video driver
  kernel centos8/vmlinuz
  append initrd=centos8/initrd.img ip=dhcp inst.xdriver=vesa nomodeset inst.repo=http://pxe.linuxsysadmins.local/centos8 ks=http://pxe.linuxsysadmins.local/ks_files/centos8-ks.cfg
label rescue
  menu label ^Rescue installed system
  kernel centos8/vmlinuz
  append initrd=centos8/initrd.img rescue
label local
  menu label Boot from ^local drive
  localboot 0xffff

We are almost done with all the setup, now allow the TFTP traffic.

# firewall-cmd --add-service=tftp --permanent 
# firewall-cmd --reload

Finally, Start the service and enable persistently to start during the reboots.

# systemctl enable tftp.service 
# systemctl start tftp.service 
# systemctl status tftp.service 

Starting with PXE Boot

Before installing my Physical node, Let me use my Physical node’s MAC address on one of the Virtual machines to test the PXE boot functionality.

Change the boot order to network and PowerON the Virtual machine, We should see as shown below.

PXE boot starting
PXE boot starting

By following it will show the menu to choose or installation will automatically starts in few seconds.

PXE boot menu
PXE boot menu

Using my Physical Node’s MAC works, Now let me try with a new Virtual machine.

Testing with Virtual Machine

Define a new static IP/Hostname and MAC in “/etc/dhcp/dhcpd.conf” for testing purpose.

host pxetest {
  option host-name "pxetest.linuxsysadmins.local";
  hardware ethernet 1C:69:7D:11:22:33;
  filename "pxelinux.0";

Restart the DHCP service by running # systemctl restart dhcpd

The DNS record (pxetest) has been created on my IDM server as well.

PXE Boot DNS Entry
PXE Boot DNS Entry

Then, PowerOn the Virtual Machine. In the meantime, log in to the PXE Boot Server and check the DHCP logs to know how the IP is offered.

[root@pxe ~]# journalctl -f _SYSTEMD_UNIT=dhcpd.service 
-- Logs begin at Mon 2020-11-23 00:55:00 +04. --
Nov 23 20:52:55 pxe.linuxsysadmins.local dhcpd[6124]: DHCPDISCOVER from 1c:69:7d:11:22:33 via ens18
Nov 23 20:52:55 pxe.linuxsysadmins.local dhcpd[6124]: DHCPOFFER on to 1c:69:7d:11:22:33 via ens18
No v 23 20:52:56 pxe.linuxsysadmins.local dhcpd[6124]: DHCPDISCOVER from 1c:69:7d:11:22:33 via ens18
Nov 23 20:52:56 pxe.linuxsysadmins.local dhcpd[6124]: DHCPOFFER on to 1c:69:7d:11:22:33 via ens18
Nov 23 20:52:58 pxe.linuxsysadmins.local dhcpd[6124]: DHCPREQUEST for ( from 1c:69:7d:11:22:33 via ens18
Nov 23 20:52:58 pxe.linuxsysadmins.local dhcpd[6124]: DHCPACK on to 1c:69:7d:11:22:33 via ens18
Nov 23 20:53:28 pxe.linuxsysadmins.local dhcpd[6124]: DHCPDISCOVER from 1c:69:7d:11:22:33 via ens18
Nov 23 20:53:28 pxe.linuxsysadmins.local dhcpd[6124]: DHCPOFFER on to 1c:69:7d:11:22:33 via ens18
[root@pxe ~]#

If you need to know how the TFTP works, run the tail -f command on the /var/log/messages logfile in our PXE Boot Server, we could see what are the files are transferred to the clients.

[root@pxe ~]# tail -f /var/log/messages
Nov 23 20:52:58 pxe systemd[1]: Started Tftp Server.
Nov 23 20:52:58 pxe in.tftpd[6176]: Client ::ffff: finished pxelinux.0
Nov 23 20:52:58 pxe in.tftpd[6177]: Client ::ffff: finished ldlinux.c32
Nov 23 20:52:58 pxe in.tftpd[6188]: Client ::ffff: finished pxelinux.cfg/default
Nov 23 20:53:04 pxe in.tftpd[6190]: Client ::ffff: finished vesamenu.c32
Nov 23 20:53:04 pxe in.tftpd[6191]: Client ::ffff: finished libcom32.c32
Nov 23 20:53:04 pxe in.tftpd[6192]: Client ::ffff: finished libutil.c32
Nov 23 20:53:04 pxe in.tftpd[6193]: Client ::ffff: finished pxelinux.cfg/default
Nov 23 20:53:11 pxe in.tftpd[6194]: Client ::ffff: finished centos8/vmlinuz
Nov 23 20:53:18 pxe in.tftpd[6195]: Client ::ffff: finished centos8/initrd.img

Watch PXE Boot Server in action

This is the VM just now installed with the help of PXE boot Server.

Configure a PXE Boot Server with Kickstart on CentOS 8 - Easy guide 1

That’s it, we have completed with the PXE boot server setup.


PXE Boot Server with kickstart will help us to automate the Operating system installation without manual interactions. Similarly, we have few other tools which will help to re-image the servers, will come up with another guide in future. Subscribe to our newsletter to get our regular updates, Feedbacks are welcome through below comment section.

One thought on “Configure a PXE Boot Server with Kickstart on CentOS 8 – Easy guide

  1. Thank you so much for this great and indeed easy tutorial!!!

    I have struggled so many nights with all kinds of errors and this just takes all of them awaY!
    Best regards.

Comments are closed.