> For the complete documentation index, see [llms.txt](https://docs.platform9.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.platform9.com/private-cloud-director/2026.8/getting-started/platform9-os/network-boot-pxe.md).

# Network Boot (PXE)

## Overview

This guide explains how to install a Platform9 OS host over the network instead of from a CD, USB stick, or virtual media. It is intended for administrators who provision bare-metal or virtual hosts at scale.

The guide covers booting the standard Platform9 OS host installer ISO over PXE, iPXE, and UEFI HTTP boot, and how each of those fits into common provisioning frameworks. It does not cover connecting the installed host to your management plane. For that, see [Platform9 OS](/private-cloud-director/2026.8/getting-started/platform9-os.md).

Three methods are described below, and all three have been validated against this ISO:

* **Method A:** classic PXE with TFTP and HTTP.
* **Method B:** iPXE with HTTP.
* **Method C:** a provisioning framework such as Cobbler, Foreman, or MAAS.

{% hint style="warning" %}
Tools that promise to boot any ISO as-is over the network (iVentoy, netboot.xyz, and the iPXE `sanboot` command) do **not** work with this installer. See [Tools That Do Not Work With This Installer](#tools-that-do-not-work-with-this-installer) for why.
{% endhint %}

In this guide, you will serve the contents of the installer ISO over HTTP, stage the netboot payload on TFTP, and boot a host into the Platform9 OS installer with the correct kernel arguments.

## Why Network Boot Requires Additional Setup

A bootable ISO is designed to be attached as a labeled block device (a CD, USB stick, or virtual media) and booted from directly. Network boot is a different model: the machine pulls only a small kernel and initrd over the network, and then has to fetch everything else from a location you specify.

Two properties of the ISO have to be adjusted for that to work. This is true of every Anaconda-based installer, including RHEL, Rocky, and Fedora:

1. **The installer runtime is located by filesystem label.** The ISO's boot entries point at `inst.stage2=hd:LABEL=RLC-PRO-10-X86_64-MINIMAL`, which only resolves when the ISO is a local block device carrying that label. Over the network, there is no such device, so you must repoint it at a URL.
2. **The bulk of the installer is not delivered by PXE.** PXE and TFTP transfer only the kernel and initrd. The installer runtime image (`images/install.img`), the package repositories, and the Platform9 OS payload must be served separately over HTTP, NFS, or FTP from a location you control.

The rest of this guide is how to serve the ISO contents and point the boot arguments at them.

## What You Need

* A **DHCP** server on the provisioning network. This can be ISC `dhcpd`, `dnsmasq`, or your existing infrastructure DHCP.
* A **TFTP** server to hand out the bootloader, kernel, and initrd.
* An **HTTP** server (Apache, nginx, or `python3 -m http.server`) to serve the full extracted ISO tree. HTTP is strongly preferred over TFTP for the large files.
* The Platform9 OS host installer ISO.
* Network reachability: the host being installed must be able to reach both the TFTP server and the HTTP server from the provisioning VLAN.

## Required Kernel Arguments

Whatever method you use, the installer must be started with the following kernel command line.

```
inst.stage2=<HTTP_BASE_URL>  inst.repo=<HTTP_BASE_URL>  inst.ks=<HTTP_BASE_URL>kickstart.processed  ip=dhcp  enforcing=0
```

| Argument                           | Purpose                                     | Notes                                                                                                                                                                                                                                                                                                                            |
| ---------------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `inst.stage2=<url>`                | Location of the installer runtime image     | Replaces the ISO's `hd:LABEL=` locator. Point it at the root of the served tree.                                                                                                                                                                                                                                                 |
| `inst.repo=<url>`                  | Location of the package repository and tree | Use the same base URL as `inst.stage2`.                                                                                                                                                                                                                                                                                          |
| `inst.ks=<url>kickstart.processed` | The kickstart file                          | Served at the root of the tree.                                                                                                                                                                                                                                                                                                  |
| `ip=dhcp`                          | Brings up networking in the initrd          | **Required**, so that the initrd can reach the HTTP server. Use the static form `ip=<client-ip>::<gw>:<mask>::<iface>:none` if the install network has no DHCP.                                                                                                                                                                  |
| `enforcing=0`                      | Runs the installer with SELinux permissive  | **Required.** The installer environment does not start under SELinux enforcing when booted over the network, and stalls with `systemd` errors. This affects the installer only; the installed system's SELinux mode is set by the kickstart, not by this argument. The media boot entries set `enforcing=0` for the same reason. |

`<HTTP_BASE_URL>` is the URL of the served tree, with a trailing slash, for example `http://mirror.example.com/pcd/`. The trailing slash matters.

{% hint style="warning" %}
**Serve the whole tree, not just the repository.** The kickstart's pre-install stage reads several root-level files from the install source: `pcd-iso-ui`, `kickstart_components/`, and `kickstart.processed`. A repository-only sync that keeps just the package directories boots the installer but fails in the pre-install stage. Serve the complete extracted ISO tree.
{% endhint %}

## Method A: PXE, TFTP, and HTTP

This is the standard, framework-independent method.

### Step 1: Extract the Full ISO Tree to Your HTTP Server

Extract the entire ISO, without repacking it, to a directory served over HTTP, for example `/var/www/html/pcd/`. Either of the following commands works, and neither requires root or a mount:

```bash
xorriso -osirrox on -indev rocky-custom-autoinstaller.<version>.iso -extract / /var/www/html/pcd
```

```bash
bsdtar -C /var/www/html/pcd -xf rocky-custom-autoinstaller.<version>.iso
```

Confirm that the tree contains the runtime image and the Platform9 OS payload:

```bash
ls /var/www/html/pcd/images/install.img      # installer runtime (stage2)
ls /var/www/html/pcd/images/pxeboot/vmlinuz  # netboot kernel
ls /var/www/html/pcd/kickstart.processed     # kickstart
ls /var/www/html/pcd/pcd-iso-ui              # payload, needed by %pre
ls -d /var/www/html/pcd/kickstart_components # payload, needed by %pre
```

Make sure the files are readable by the web server, then verify reachability from the client's network:

```bash
curl -sfI http://mirror.example.com/pcd/images/install.img && echo REACHABLE
curl -sfI http://mirror.example.com/pcd/kickstart.processed && echo KS-REACHABLE
```

### Step 2: Stage the Netboot Payload on TFTP

Copy the kernel and initrd to your TFTP root, along with the UEFI bootloader from the ISO. Shipping the ISO's own signed shim and GRUB keeps Secure Boot intact:

```bash
cp /var/www/html/pcd/images/pxeboot/vmlinuz    /var/lib/tftpboot/pcd/
cp /var/www/html/pcd/images/pxeboot/initrd.img /var/lib/tftpboot/pcd/
cp -r /var/www/html/pcd/EFI/BOOT               /var/lib/tftpboot/pcd/EFI-BOOT
```

### Step 3: Configure DHCP

Point PXE clients at your TFTP server and hand out the correct bootloader for each firmware type. The following is an ISC `dhcpd` example:

```
option client-system-arch code 93 = unsigned integer 16;

class "pxeclients" {
    match if substring(option vendor-class-identifier, 0, 9) = "PXEClient";
    next-server <TFTP_SERVER_IP>;
    if option client-system-arch = 00:07 or option client-system-arch = 00:09 {
        filename = "pcd/EFI-BOOT/BOOTX64.EFI";   # UEFI: signed shim -> grub
    } else {
        filename = "pcd/pxelinux.0";             # BIOS: from the syslinux package
    }
}
```

For BIOS clients, you supply `pxelinux.0` from the `syslinux` or `syslinux-tftpboot` package, because it is not on the ISO. UEFI clients use the ISO's own signed `BOOTX64.EFI`.

### Step 4: Write the Bootloader Configuration

For **UEFI**, write the following GRUB configuration at `/var/lib/tftpboot/pcd/EFI-BOOT/grub.cfg`:

```
set timeout=5
menuentry 'Install Platform9 OS Host' {
  linuxefi vmlinuz inst.stage2=http://mirror.example.com/pcd/ inst.repo=http://mirror.example.com/pcd/ inst.ks=http://mirror.example.com/pcd/kickstart.processed ip=dhcp enforcing=0 quiet
  initrdefi initrd.img
}
```

For **BIOS**, write the following PXELINUX configuration at `/var/lib/tftpboot/pcd/pxelinux.cfg/default`:

```
default pcd
prompt 0
timeout 50
label pcd
  menu label Install Platform9 OS Host
  kernel vmlinuz
  append initrd=initrd.img inst.stage2=http://mirror.example.com/pcd/ inst.repo=http://mirror.example.com/pcd/ inst.ks=http://mirror.example.com/pcd/kickstart.processed ip=dhcp enforcing=0 quiet
```

### Step 5: Boot the Host

Set the target host to network boot. It fetches the kernel and initrd over TFTP, brings up networking, pulls the runtime image and repository over HTTP, and starts the Platform9 OS installer wizard.

## Method B: iPXE and HTTP

TFTP is slow for large files. If your environment already has iPXE, or you chainload it, keep only the iPXE binary and the kernel and initrd references on TFTP and let iPXE pull everything else over HTTP:

```
#!ipxe
dhcp
kernel http://mirror.example.com/pcd/images/pxeboot/vmlinuz initrd=initrd.img inst.stage2=http://mirror.example.com/pcd/ inst.repo=http://mirror.example.com/pcd/ inst.ks=http://mirror.example.com/pcd/kickstart.processed ip=dhcp enforcing=0 quiet
initrd http://mirror.example.com/pcd/images/pxeboot/initrd.img
boot
```

The kernel arguments are identical to Method A. Only the transport changes.

## Method C: Provisioning Frameworks

At rack scale, use a provisioning framework rather than maintaining TFTP and DHCP by hand. Each of the following imports the installer tree and generates the boot configuration for you:

* **Cobbler:** run `cobbler import --name=pcd --arch=x86_64 --path=/mnt/pcd-iso`, then add the kernel arguments from [Required Kernel Arguments](#required-kernel-arguments) to the profile's kernel options.
* **Foreman or Red Hat Satellite:** add the extracted tree as an installation medium and set the kernel options on the host group.
* **MAAS:** register the image and set the extra kernel parameters.

In every case, set the same kernel arguments described above, including `enforcing=0` and `ip=dhcp`, in the framework's kernel-options field.

## Tools That Do Not Work With This Installer

Tools that promise to network-boot any ISO file with no configuration (iVentoy, netboot.xyz, and the iPXE `sanboot` command) do **not** work for this installer. Each was tested directly against this ISO, and each fails, because this installer uses a boot model that these generic media-emulation tools do not handle. Use Method A, B, or C instead.

* **iVentoy** presents the whole ISO to the client as a virtual network disk. The installer's kernel boots, but the `inst.stage2=hd:LABEL=` locator does not resolve against the emulated disk, so dracut times out waiting for `/dev/root` and the install fails. iVentoy also cannot run alongside an existing DHCP server without additional network isolation.
* **iPXE `sanboot`** downloads the entire ISO into a RAM disk before booting it. This ISO exceeds the iPXE sanboot size limit and is rejected up front with `Could not open SAN device: Result too large`.
* **netboot.xyz** is a curated iPXE menu of mainstream operating system installers pulled from official mirrors. It does not include custom vendor ISOs, and its built-in iPXE shell hits the same `sanboot` limitation described above.

The reliable, supported ways to network-boot this ISO are Method A, Method B, and Method C. All three fetch the kernel and initrd and boot with the explicit arguments from [Required Kernel Arguments](#required-kernel-arguments), which is exactly what the installer needs.

## Unattended Installation at Scale

By default, the installer pauses for an interactive setup wizard covering the keyboard layout, root password, target disk, and hostname. This is convenient for a single host, but it stops every machine in an unattended run.

To provision many hosts fully hands-off, supply your own kickstart in place of the interactive wizard. See [Unattended Installation (Kickstart)](/private-cloud-director/2026.8/getting-started/platform9-os/unattended-install-kickstart.md), which builds on the PXE environment described in this guide.

## Troubleshooting

| Symptom                                                                                                                                                           | Cause                                                                                     | Fix                                                                                                                                                                       |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| The boot hangs on `dracut` waiting for `/dev/root`, or reports a missing `inst.stage2` or `inst.repo`                                                             | The `hd:LABEL=` locator is still in use, or the stage2 URL is unreachable from the client | Use the URL arguments described in [Required Kernel Arguments](#required-kernel-arguments), and verify with `curl -sfI <url>images/install.img` from the client's network |
| The installer starts, then the pre-install stage reports that it cannot stat `pcd-iso-ui` or a similar path                                                       | Only the repository is served, rather than the full tree                                  | Serve the complete extracted ISO tree, including `pcd-iso-ui`, `kickstart_components/`, and `kickstart.processed`                                                         |
| The boot loads the runtime, then `systemd` prints repeated `Unable to fix SELinux security context ... Permission denied` errors followed by `Freezing execution` | `enforcing=0` is missing                                                                  | Add `enforcing=0` to the kernel arguments                                                                                                                                 |
| The installer cannot reach the URL at all                                                                                                                         | `ip=dhcp` is missing, or HTTP is not reachable on the install VLAN                        | Add `ip=dhcp`, and confirm that the HTTP host is reachable from the client's network, not only from the TFTP server                                                       |
| Trailing-slash or path errors occur while fetching the tree                                                                                                       | The base URL on `inst.stage2` or `inst.repo` is missing its trailing slash                | Ensure the base URL ends with `/`                                                                                                                                         |

## Next Steps

* [Unattended Installation (Kickstart)](/private-cloud-director/2026.8/getting-started/platform9-os/unattended-install-kickstart.md): replace the interactive wizard with a kickstart you supply.
* [Platform9 OS](/private-cloud-director/2026.8/getting-started/platform9-os.md): connect the installed host to your management plane and authorize it.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.platform9.com/private-cloud-director/2026.8/getting-started/platform9-os/network-boot-pxe.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
