Skip to content

Install Using PXE Boot

Automate bare-metal Garden Linux deployments using iPXE network boot combined with Ignition for first-boot configuration. This guide covers the complete PXE installation workflow, from network boot to disk installation.

For non-network installations using bootable media, see Install Using ISO.

Overview

The PXE installation process boots Garden Linux as a live system over the network, applies configuration via Ignition, and installs the system to disk. The workflow consists of:

  1. Network boot via iPXE — Client firmware loads iPXE from TFTP, which fetches the kernel, initrd, and squashfs root filesystem over HTTP
  2. Live system — Garden Linux runs from an OverlayFS with the squashfs image as the lower layer and tmpfs as the upper layer
  3. Ignition configuration — Applies first-boot configuration (users, files, services)
  4. Installation — Copies the live system to disk, installs the bootloader, and kexec into the installed system
  5. Subsequent boots — System boots directly from disk without PXE

First Boot Flow

mermaid
flowchart TD
    A[Power On] --> B[Firmware: BIOS/UEFI]
    B --> C[PXE Boot]
    C --> D[DHCP: Get IP + TFTP server]
    D --> E[TFTP: Download iPXE binary]
    E --> F[iPXE: Get boot script URL via DHCP]
    F --> G[HTTP: Download boot.ipxe]
    G --> H[HTTP: Download kernel + initrd]
    H --> I[Boot Kernel]
    I --> J[Initramfs: Fetch squashfs via HTTP]
    J --> K[Mount OverlayFS: squashfs + tmpfs]
    K --> L[Ignition: Fetch config from URL]
    L --> M[Ignition: Write files, enable services]
    M --> N[install.service: Partition disk]
    N --> O[install.service: Copy rootfs to disk]
    O --> P[install.service: Install bootloader]
    P --> Q[kexec into installed system]
    Q --> R[Garden Linux running from disk]

Subsequent Boot Flow

mermaid
flowchart TD
    A[Power On] --> B[Firmware: BIOS/UEFI]
    B --> C[Boot from Disk]
    C --> D[Bootloader: syslinux or systemd-boot]
    D --> E[Garden Linux]

Network Boot Sequence

mermaid
sequenceDiagram
    participant Client
    participant DHCP
    participant TFTP
    participant HTTP
    
    Client->>DHCP: Request IP address
    DHCP-->>Client: IP + TFTP server + filename
    Note over Client,DHCP: Filename: ipxe.efi (UEFI) or undionly.kpxe (BIOS)
    
    Client->>TFTP: Download iPXE binary
    TFTP-->>Client: iPXE binary
    
    Client->>DHCP: Request boot script URL (iPXE environment)
    DHCP-->>Client: HTTP URL to boot.ipxe
    
    Client->>HTTP: GET boot.ipxe
    HTTP-->>Client: boot.ipxe script
    
    Client->>HTTP: GET rootfs.vmlinuz
    HTTP-->>Client: Kernel image
    
    Client->>HTTP: GET rootfs.initrd
    HTTP-->>Client: Initramfs
    
    Client->>Client: Boot kernel
    
    Client->>HTTP: GET root.squashfs
    HTTP-->>Client: Root filesystem image
    
    Client->>HTTP: GET ignition.json
    HTTP-->>Client: Ignition configuration
    
    Client->>HTTP: GET install.json (via ignition merge)
    HTTP-->>Client: Install configuration
    
    Client->>Client: Install to disk

Prerequisites

  • Target clients — Physical servers or virtual machines with BIOS or UEFI firmware supporting network boot
  • Garden Linux build — Image built with _pxe, metal, and server features, generating:
    • rootfs.vmlinuz — Kernel image
    • rootfs.initrd — Initramfs with live boot support
    • root.squashfs — Compressed root filesystem
  • TFTP server — Serves iPXE binaries for Legacy BIOS and UEFI firmware:
    • undionly.kpxe — Legacy BIOS iPXE binary
    • ipxe.efi — UEFI iPXE binary
  • DHCP server — Provides IP addresses and PXE chainloading configuration
  • HTTP server — Hosts Garden Linux images, iPXE boot scripts, and Ignition configuration files

Download iPXE binaries from https://boot.ipxe.org.

Building PXE Images

For detailed build system documentation and building PXE images with custom features, see Building Images.

Disk Layout and Bootloader

The installation creates a GPT partition table with two partitions:

PartitionTypeSizeFormatPurpose
EFIC12A7328-F81F-11D2-BA4B-00A0C93EC93B (ESP)510 MiBFAT32Bootloader storage (Legacy: syslinux stages, UEFI: systemd-boot + UKI)
ROOT0FC63DAF-8483-4772-8E79-3D69D8477DE4 (Linux filesystem)Remaining spaceext4Garden Linux root filesystem

Bootloader Installation

The installation script detects the firmware type and installs the appropriate bootloader:

  • Legacy BIOS — Installs syslinux. Writes a new MBR to the first sector of the disk and installs syslinux stages 2+ to the EFI partition.
  • UEFI — Installs systemd-boot with a Unified Kernel Image (UKI). A UKI is a single EFI PE executable combining an EFI stub loader, kernel image, initramfs, and kernel command line. UKI images can be signed for Secure Boot.

This identical disk layout for both firmware types allows migrating an installation between Legacy and UEFI systems by re-running the appropriate bootloader installation script.

Configure the iPXE Boot Script

Create an iPXE boot script (boot.ipxe) that instructs iPXE to fetch the Garden Linux kernel, initramfs, and root filesystem:

bash
#!ipxe

set base-url http://192.168.1.10:8080
kernel ${base-url}/rootfs.vmlinuz initrd=rootfs.initrd \
  gl.ovl=/:tmpfs \
  gl.url=${base-url}/root.squashfs \
  gl.live=1 \
  ip=dhcp \
  console=ttyS1,115200n8 console=tty0 \
  earlyprintk=ttyS1,115200n8 consoleblank=0 \
  ignition.firstboot=1 \
  ignition.config.url=${base-url}/ignition.json \
  ignition.platform.id=metal
initrd ${base-url}/rootfs.initrd
boot

Kernel Parameters Explained

  • gl.ovl=/:tmpfs — Use tmpfs as the upper layer for the OverlayFS root filesystem
  • gl.url=${base-url}/root.squashfs — URL to the compressed root filesystem image
  • gl.live=1 — Enable live boot mode (do not persist changes to disk during live session)
  • ip=dhcp — Configure network interfaces via DHCP
  • console=ttyS1,115200n8 console=tty0 — Output to serial console (ttyS1) and VGA console (tty0)
  • ignition.firstboot=1 — Tell Ignition this is a first boot (Ignition only runs on first boot)
  • ignition.config.url=${base-url}/ignition.json — URL to the Ignition configuration file
  • ignition.platform.id=metal — Platform identifier for Ignition (use metal for bare-metal and non-cloud deployments)

Replace http://192.168.1.10:8080 with your HTTP server's address.

Configure Ignition

Ignition configures the system during first boot before the installation begins. Create two Ignition configuration files:

  1. ignition.yaml — Main configuration defining partition layout, target disk, and merging the install configuration
  2. install.yaml — Installation-specific configuration provided by Garden Linux

Create ignition.yaml

Create the main Ignition configuration in YAML format:

yaml
variant: fcos
version: 1.3.0
ignition:
  config:
    merge:
      - source: http://192.168.1.10:8080/install.json
storage:
  files:
    - path: /opt/onmetal-install/partitions
      overwrite: true
      mode: 0755
      contents:
        inline: |
          label: gpt
          type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, name="EFI", size=510MiB
          type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, name="ROOT"
    - path: /opt/onmetal-install/target
      overwrite: true
      mode: 0755
      contents:
        inline: |
          disk=/dev/sda

Configuration Fields

  • ignition.config.merge — Merges the installation configuration from the specified URL. This must point to install.json (the translated install.yaml provided by Garden Linux).
  • /opt/onmetal-install/partitions — Defines the partition layout. The first two partitions (EFI and ROOT) must match the format shown. Additional partitions can be appended after ROOT if needed.
  • /opt/onmetal-install/target — Specifies the target disk for installation (e.g., /dev/sda, /dev/nvme0n1).

To add users, SSH keys, hostname, systemd units, or other configuration to ignition.yaml, see the Provision with Ignition guide for the full range of Ignition configuration options.

To translate the YAML configuration to JSON, use Butane.

Set Up Network Boot Infrastructure

Configure DHCP Server

Configure your DHCP server to provide PXE chainloading. Example configuration for dnsmasq:

ini
# Enable DHCP
dhcp-range=192.168.1.100,192.168.1.200,12h

# PXE boot configuration
dhcp-match=set:bios,option:client-arch,0
dhcp-match=set:efi64,option:client-arch,7
dhcp-match=set:efi64,option:client-arch,9

# BIOS clients: chainload to iPXE
dhcp-boot=tag:bios,undionly.kpxe,192.168.1.10

# UEFI clients: chainload to iPXE
dhcp-boot=tag:efi64,ipxe.efi,192.168.1.10

# iPXE clients: provide boot script URL
dhcp-match=set:ipxe,175
dhcp-boot=tag:ipxe,http://192.168.1.10:8080/boot.ipxe

Replace 192.168.1.10 with your server's IP address.

Configure TFTP Server

Set up a TFTP server to serve iPXE binaries. Example using tftpd-hpa:

bash
# Install TFTP server
apt-get install tftpd-hpa

# Download iPXE binaries
cd /var/lib/tftpboot
curl -O https://boot.ipxe.org/undionly.kpxe
curl -O https://boot.ipxe.org/ipxe.efi

# Restart TFTP service
systemctl restart tftpd-hpa

Configure HTTP Server

Set up an HTTP server to host Garden Linux images and configuration files. Example using nginx:

bash
# Install nginx
apt-get install nginx

# Create directory structure
mkdir -p /var/www/pxe
cd /var/www/pxe

# Copy Garden Linux images
cp /path/to/rootfs.vmlinuz .
cp /path/to/rootfs.initrd .
cp /path/to/root.squashfs .

# Copy configuration files
cp boot.ipxe .
cp ignition.json .
cp install.json .

# Configure nginx
cat > /etc/nginx/sites-available/pxe <<'EOF'
server {
    listen 8080;
    root /var/www/pxe;
    autoindex on;
    
    location / {
        try_files $uri $uri/ =404;
    }
}
EOF

ln -s /etc/nginx/sites-available/pxe /etc/nginx/sites-enabled/
systemctl reload nginx

Boot the Target System

  1. Configure BIOS/UEFI — Enable network boot in the firmware settings and set network boot as the first boot device
  2. Power on the system — The system boots via PXE and fetches the iPXE binary from TFTP
  3. iPXE loads — iPXE fetches boot.ipxe from the HTTP server and boots the Garden Linux kernel
  4. Live system starts — The initramfs downloads root.squashfs and mounts it as an OverlayFS
  5. Ignition runs — Fetches and applies the Ignition configuration
  6. Installation begins — The install.service systemd unit partitions the disk, copies the live system to disk, and installs the bootloader
  7. System reboots — kexec switches into the installed system running from disk

The installation typically completes in 5-10 minutes depending on network speed and disk performance.

Post-Installation

After installation, the system boots directly from disk without PXE. The dracut module responsible for live booting is disabled, and the installer is removed.

To configure the installed system:

  • Enable SSH — SSH is disabled by default. Enable it via Ignition (add ssh.service to systemd units) or manually after installation
  • Create users — Add users via Ignition or use the recovery procedure described in Post-Install Configuration
  • Configure networking — Network configuration can be defined in Ignition using systemd-networkd configuration files

Troubleshooting

System Does Not Boot via PXE

  • Verify network boot is enabled — Check BIOS/UEFI settings
  • Check DHCP responses — Use tcpdump on the DHCP server to verify PXE DHCP responses:
    bash
    tcpdump -i eth0 -n port 67 and port 68
  • Verify TFTP server accessibility — Test TFTP from another machine:
    bash
    tftp 192.168.1.10
    get undionly.kpxe

iPXE Fails to Download Files

  • Check HTTP server logs — Verify requests are reaching the server:
    bash
    tail -f /var/log/nginx/access.log
  • Test HTTP URLs manually — Verify files are accessible:
    bash
    curl http://192.168.1.10:8080/boot.ipxe
    curl http://192.168.1.10:8080/rootfs.vmlinuz
  • Verify file permissions — Ensure files are readable by the web server

Ignition Configuration Not Applied

  • Check Ignition logs — After booting the live system, check Ignition journal entries:
    bash
    journalctl -u ignition-fetch.service
    journalctl -u ignition-files.service
  • Validate Ignition JSON syntax — Use Butane to validate the YAML before translation:
    bash
    ./butane --strict ignition.yaml > /dev/null
  • Verify ignition.config.url is reachable — Ensure the URL in boot.ipxe is correct and accessible from the target system

Installation Fails or Does Not Start

  • Check install.service logs:
    bash
    journalctl -u install.service
  • Verify target disk exists — Check that the disk specified in /opt/onmetal-install/target exists:
    bash
    lsblk
    cat /opt/onmetal-install/target
  • Check partition layout — Verify /opt/onmetal-install/partitions syntax is correct

System Boots Back to PXE After Installation

  • UEFI boot order not updated — The installation should update the UEFI boot order. If it does not, manually set the disk as the first boot device in firmware settings
  • Bootloader installation failed — Check installation logs for bootloader errors

Reference