⇦ Back

Network booting Raspberry Pis

Set up the client device

You’ll need to add network booting to the boot order of your Raspberry Pi. An easy way to do this on Pi 5s is to use the Raspberry Pi Imager’s “Network Boot” image which’ll handle all the eeprom configuration for you.

You’ll only need to do this once unless you remove network booting from your boot order in the future.

Raspberry Pi network boot flow

DHCP

When the Pi attempts to network boot it will send a DHCP discover request that looks like this:

Frame 6044: 364 bytes on wire (2912 bits), 364 bytes captured (2912 bits) on interface sshdump, id 0
Ethernet II, Src: f6:92:bf:91:13:a6 (f6:92:bf:91:13:a6), Dst: RaspberryPiF_64:46:1c (b8:27:eb:64:46:1c)
Internet Protocol Version 4, Src: 10.0.0.1, Dst: 10.0.0.75
User Datagram Protocol, Src Port: 67, Dst Port: 67
Dynamic Host Configuration Protocol (Discover)
    Message type: Boot Request (1)
    Hardware type: Ethernet (0x01)
    Hardware address length: 6
    Hops: 1
    Transaction ID: 0x0a485863
    Seconds elapsed: 0
    Bootp flags: 0x0000 (Unicast)
        0... .... .... .... = Broadcast flag: Unicast
        .000 0000 0000 0000 = Reserved flags: 0x0000
    Client IP address: 0.0.0.0
    Your (client) IP address: 0.0.0.0
    Next server IP address: 0.0.0.0
    Relay agent IP address: 10.0.0.1
    Client MAC address: RaspberryPiT_e8:27:1e (d8:3a:dd:e8:27:1e)
    Client hardware address padding: 00000000000000000000
    Server host name not given
    Boot file name not given
    Magic cookie: DHCP
    Option: (53) DHCP Message Type (Discover)
        Length: 1
        DHCP: Discover (1)
    Option: (55) Parameter Request List
        Length: 14
        Parameter Request List Item: (1) Subnet Mask
        Parameter Request List Item: (3) Router
        Parameter Request List Item: (43) Vendor-Specific Information
        Parameter Request List Item: (60) Vendor class identifier
        Parameter Request List Item: (66) TFTP Server Name
        Parameter Request List Item: (67) Bootfile name
        Parameter Request List Item: (128) DOCSIS full security server IP [TODO]
        Parameter Request List Item: (129) PXE - undefined (vendor specific)
        Parameter Request List Item: (130) PXE - undefined (vendor specific)
        Parameter Request List Item: (131) PXE - undefined (vendor specific)
        Parameter Request List Item: (132) PXE - undefined (vendor specific)
        Parameter Request List Item: (133) PXE - undefined (vendor specific)
        Parameter Request List Item: (134) PXE - undefined (vendor specific)
        Parameter Request List Item: (135) PXE - undefined (vendor specific)
    Option: (60) Vendor class identifier
        Length: 32
        Vendor class identifier: PXEClient:Arch:00000:UNDI:002001
    Option: (93) Client System Architecture
        Length: 2
        Client System Architecture: IA x86 PC (0)
    Option: (94) Client Network Device Interface
        Length: 3
        Major Version: 2
        Minor Version: 1
    Option: (97) UUID/GUID-based Client Identifier
        Length: 17
        Client Identifier (UUID): 52506935-4170-00d0-dde8-271e64f2093a
    Option: (255) End
        Option End: 255

To get it to netboot you’ll need to respond with two particular DHCP options set:

  • Option 44 must contain “Raspberry Pi Boot”
  • Option 66 must contain the IP address of the TFTP server.

It’s worth noting here that, unlike many other clients, Raspberry Pis ignore the next server and boot file fields in the response.

Here’s an example response from my dhcpd server:

Frame 6048: 342 bytes on wire (2736 bits), 342 bytes captured (2736 bits) on interface sshdump, id 0
Ethernet II, Src: RaspberryPiF_64:46:1c (b8:27:eb:64:46:1c), Dst: RaspberryPiT_e8:27:1e (d8:3a:dd:e8:27:1e)
Internet Protocol Version 4, Src: 10.0.0.75, Dst: 10.0.0.240
User Datagram Protocol, Src Port: 67, Dst Port: 68
Dynamic Host Configuration Protocol (Offer)
    Message type: Boot Reply (2)
    Hardware type: Ethernet (0x01)
    Hardware address length: 6
    Hops: 0
    Transaction ID: 0x0a485863
    Seconds elapsed: 0
    Bootp flags: 0x0000 (Unicast)
        0... .... .... .... = Broadcast flag: Unicast
        .000 0000 0000 0000 = Reserved flags: 0x0000
    Client IP address: 0.0.0.0
    Your (client) IP address: 10.0.0.240
    Next server IP address: 0.0.0.0
    Relay agent IP address: 0.0.0.0
    Client MAC address: RaspberryPiT_e8:27:1e (d8:3a:dd:e8:27:1e)
    Client hardware address padding: 00000000000000000000
    Server host name not given
    Boot file name: bootcode.bin
    Magic cookie: DHCP
    Option: (53) DHCP Message Type (Offer)
        Length: 1
        DHCP: Offer (2)
    Option: (54) DHCP Server Identifier (10.0.0.75)
        Length: 4
        DHCP Server Identifier: 10.0.0.75
    Option: (51) IP Address Lease Time
        Length: 4
        IP Address Lease Time: 1 day (86400)
    Option: (1) Subnet Mask (255.255.255.0)
        Length: 4
        Subnet Mask: 255.255.255.0
    Option: (3) Router
        Length: 4
        Router: 10.0.0.1
    Option: (43) Vendor-Specific Information
        Length: 17
        Value: 52617370626572727920506920426f6f74
    Option: (66) TFTP Server Name
        Length: 9
        TFTP Server Name: 10.0.0.75
    Option: (255) End
        Option End: 255
    Padding: 0000

TFTP

Now that the Pi knows which TFTP server to talk to it’ll try to fetch the files it needs to boot.

First it’ll look for config.txt in a subdirectory based on the Pi’s serial number, for example: 3a09f264/config.txt.

If that file isn’t present it’ll look for config.txt in the root directory, for example: config.txt.

From there it will request different files depending on the Pi model and the configuration in config.txt:

Here’s an example of the TFTP requests from a Pi 5:

TFTP Read Request, File: 3a09f264/config.txt
TFTP Read Request, File: config.txt
TFTP Read Request, File: pieeprom.sig
TFTP Read Request, File: bcm2712-rpi-5-b.dtb
TFTP Read Request, File: /config.txt
TFTP Read Request, File: /config.txt
TFTP Read Request, File: kernel_2712.img
TFTP Read Request, File: bcm2712-rpi-5-b.dtb
TFTP Read Request, File: /overlays/overlay_map.dtb
TFTP Read Request, File: /config.txt
TFTP Read Request, File: /overlays/dwc2.dtbo
TFTP Read Request, File: /cmdline.txt
TFTP Read Request, File: armstub8-2712.bin
TFTP Read Request, File: kernel_2712.img

Configure dhcpd

On my network I still use dhcpd to handle my IPv4 devices .

Here’s an minimal example of a dhcpd.conf configuration that’ll netboot Raspberry Pis as well as handling other PXE clients.

# Declare the PXE client class used to detect client architecture.
option pxe-system-type code 93 = unsigned integer 16;

# Declare the vendor-specific option for Raspberry Pis.
option raspberry-pi-boot code 43 = text;

subnet 10.0.0.0 netmask 255.255.255.0 {
    option routers 10.0.0.1;

    # Put the IP of your TFTP server here.
    option tftp-server-name "10.0.0.75";
    
    pool {
        range 10.0.0.200 10.0.0.254;
        allow unknown-clients;
    }

    # Class for PXE clients
    class "pxeclients" {
        # Match PXE client devices
        match if substring (option vendor-class-identifier, 0, 9) = "PXEClient";
        
        # If the first three bytes are in a block of Raspberry Pi MACs then set the Pi-specific option
        if substring (hardware, 1, 3) = d8:3a:dd {
            option raspberry-pi-boot "Raspberry Pi Boot";
        } else if option pxe-system-type = 00:07 {
            # This assumes your tftp server has a boot subdirectory with Grub's EFI files in it.
            filename "boot/grub/x86_64-efi/core.efi";
        } else if option pxe-system-type = 00:08 {
            filename "boot/grub/x86_64-efi/core.efi";
        } else if option pxe-system-type = 00:09 {
            filename "boot/grub/x86_64-efi/core.efi";
        } else if option pxe-system-type = 00:0a {
            filename "boot/grub/arm-efi/core.efi";
        } else if option pxe-system-type = 00:0b {
            filename "boot/grub/aarch64-efi/core.efi";
        } else {
            # Fall back to assuming an X86 device with only BIOS boot support
            filename "boot/grub/x86_64-efi/core.0";
        }
    }
}