Boot Ubuntu via http/ftp server with pxe(diskless boot)


PXE is a great solution for booting a diskless computer (or a computer without an OS installed). This method is often used for terminal stations and OS mass installation.

Stock ubuntu (16.04) in pxe-mode can mount rootfs only from NFS. But this is not a great idea: any difficulties with the network/NFS server and the user gets problems.

In my opinion, it’s best to use other protocols, such as http/ftp. Once booting, you will have an independent system

You should add information about the limits of applicability of the proposed solution and what are the dependencies and restrictions.

Pxe short info

PXE (Preboot Execution Environment) is a special method for booting a computer from the bios / EFI.
How it works (simplified scheme):

  • The computer bios/uefi sends ordinary dhcp request (DHCPDISCOVER)
  • The dhcp server sends a response with the next-server option
  • The computer sends DHCPREQUEST/DHCPINFORM request
  • The dhcp server sends TFTP server address and the filename to upload
  • The computer downloads this file from the tftp server. Its size is limited so, often, it’s a bootloader like pxeinux
  • pxelinux reads its own config and downloads Linux kernel using initramfs
  • Linux kernel downloads squashfs with main rootfs
  • switch_root to its squashfs

Keep in mind that TFTP is a slow protocol. It works around UDP with a small block size (512K). Of course, you can increase this value, but this is a way of unstable operation.

A better solution:

  • get bootloader via tftp
  • get kernel (+ initramfs) via tftp
  • get main rootfs squash via http/tftp

How i do it

Steps to solve the task:

  1. Add modules to initramfs
  2. Write my own boot script and add it to initramfs
  3. Make new initramfs
  4. Create squashfs with future rootfs
  5. Setup pxe server
  6. Run it

I used squashfs for rootfs (the simplest way is to create squashfs from installed ubuntu). Overlayfs is necessary to make rootfs writable.

Supported protocols are http/ftp, but you can try to add others via curl/other software.

Customize initramfs

There are 2 places where you can customize initramfs in ubuntu:

I’ll use /usr/share/initramfs-tools. First, I added needed support modules in initramfs:

boozlachu@comp:~$ cat /usr/share/initramfs-tools/modules.d/pxe overlayfs squashfs

Next, I wrote a boot script that does all the work:

boozlachu@comp:~$ cat /usr/share/initramfs-tools/scripts/pxe#!/bin/bashmountroot(){maxTryCount=5squashfsFile="/tmp/rootfs.squashfs"squashfsMountPoint="/mnt/ro"tmpfsMountPoint="/mnt/rw"overlayfsUppderDir="$tmpfsMountPoint/upper"overlayfsWorkDir="$tmpfsMountPoint/work"overlayfsDir="/mnt/overlayfs"tryCount="1"# run udevadmwait_for_udev 10# parce kernel cmdline args. rooturl neededfor x in $(cat /proc/cmdline); docase $x inrooturl=*)export rooturl=${x#rooturl=};;maxTryCount=*)export maxTryCount=${x#maxTryCount=};;esacdonelog_begin_msg "Loading modules"modprobe squashfs || panic "can't modprobe squashfs"modprobe af_packet || panic "can't modprobe af_packet"modprobe overlay || panic "can't modprobe overlayfs"log_success_msg "modules loaded"log_begin_msg "Configure network"configure_networking || panic "Can't configure network"log_success_msg "Network configured"log_begin_msg "Download rootfs"while [ ! -f ${squashfsFile} ] && [ ${tryCount} -le ${maxTryCount} ]; dowget ${rooturl} -O ${squashfsFile} || log_failure_msg "Can't download rootfs, count ${tryCount}"tryCount=$(( ${tryCount} + 1 ))sleep 0.5doneif [ -f ${squashfsFile} ]thenlog_success_msg "Rootfs downloaded"elsepanic "Can't download rootfs"filog_begin_msg "Mount rootfs"mkdir -p $squashfsMountPointmount -t squashfs -o loop $squashfsFile $squashfsMountPoint || panic "Can't mount rootfs"log_success_msg "Rootfs mounted"log_begin_msg "Mount tmpfs"mkdir -p $tmpfsMountPointmount -t tmpfs none $tmpfsMountPoint || panic "Tmpfs mount failed "log_success_msg "Tmpfs mounted"log_begin_msg "Mount overlayfs"mkdir -p $overlayfsUppderDir $overlayfsWorkDir $overlayfsDirmount -t overlay overlay -o lowerdir=$squashfsMountPoint,upperdir=$overlayfsUppderDir,workdir=$overlayfsWorkDir $overlayfsDir \|| panic "Overlayfs mount failed"log_success_msg "Overlayfs mounted"log_begin_msg "Move tmpfs and squashfs to new root"mkdir -p $overlayfsDir/$tmpfsMountPoint $overlayfsDir/$squashfsMountPointmount --move $squashfsMountPoint $overlayfsDir/$squashfsMountPoint  || panic "squashfs move failed"mount --move $tmpfsMountPoint $overlayfsDir/$tmpfsMountPoint  || panic "tmpfs move failed"log_success_msg "Tmpfs and squashfs moved"log_begin_msg "Move overlayfs to new root"mount --move $overlayfsDir ${rootmnt}  || panic ""}

After the modules and script, add your need to generate new initramfs:

boozlachu@comp:~$ sudo update-initramfs -c -k all
  • install ubuntu on drive
  • boot from LiveCD
  • create squashfs from the installed system

I don’t recommend this way for production since you’ll have a very large squashfs (not the best idea for pxe)!

Setup bootloader, squashfs, and pxe server

TFRP server file struct (/srv/tftp is root dir fot tftp-hpa):

root@debian:/srv/tftp/ubuntu# tree /srv/tftp/ /srv/tftp/ └── ubuntu ├── firmware.sq ├── initrd ├── ldlinux.c32 ├── libcom32.c32 ├── libutil.c32 ├── menu.c32 ├── pxelinux.bin ├── pxelinux.cfg │ └── default ├── vesamenu.c32 └── vmlinuz
  • firmware.sq is squashfs with rootfs
  • *c32 are files for pxelinux
  • vmlinuz is kernel
  • initrd is initramfs(which i rebuild earler)
  • pxelinux.bin — main pxelinux file
  • default is config for pxelinux

Bootloader config:

root@debian:/srv/tftp/ubuntu# cat /srv/tftp/ubuntu/pxelinux.cfg/default ui menu.c32 timeout 30 default ubuntu_pxe font UniCyr_8x16.psf menu title PXE Special Boot Menu menu color tabmsg 37;40 #80ffffff #00000000 menu color hotsel 30;47 #40000000 #20ffffff menu color sel 30;47 #40000000 #20ffffff menu color scrollbar 30;47 #40000000 #20ffffff LABEL ubuntu_pxe menu label Run ubuntu pxe kernel vmlinuz append initrd=initrd rooturl= boot=pxe maxTryCount=10

It’s impotant to set the correct kernel parameters:

And the last one is the dhcp config:

root@debian:/srv/tftp/ubuntu# cat /etc/dhcp/dhcpd.conf subnet netmask { range; option routers; option domain-name-servers ; option broadcast-address; default-lease-time 3600; max-lease-time 7200; # Important! Set bootloader file filename "ubuntu/pxelinux.bin"; }

The extended variant (if the dhcp and tftp servers placed on different machines) requires the next-server option for dhcp.


This article shows you how to change the boot mode of ubuntu without any difficulties. Use it as information and write your solutions. This can be a system in the form of firmware (with squashfs), pxe, or another solution.

Originally published at




IT-engineer, Linux lover

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Why I stopped using else statements and you should do it !

How to Run User Acceptance Testing using Azure DevOps?

Competent Developers Are Underpaid — Here is How to Fix It

What is localization testing and why is it important?

The Cosmonaut Quest — Week4 + Month1 Review

5 Whys — how we conduct blameless post-mortems after something goes wrong

Creating Self Hosted GitHub runners in a Kubernetes Cluster

Processing Excel Data with Powershell

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store


IT-engineer, Linux lover

More from Medium

How Secure is Your Linux PC ?

Correct file ownership when mounting local folders in Docker on Linux

Upgrade cURL on Ubuntu Server

How to Lighten Colors Programmatically