As one of my boot drives failed, i had to reinstall esxi. I did not want to go throught the hassle of creating a bootable usb drive and decided to give PXE boot a try.

While there are a lot of articles on manually creating a tftp server for PXE boot, netboot.xyz is by far the easiest way to go about setting up PXE boot for various operating systems.

from https://netboot.xyz

“netboot.xyz lets you PXE boot various operating system installers or utilities from a single tool over the network. This lets you use one media for many types of operating systems or tools. The iPXE project is used to provide a user friendly menu from within the BIOS that lets you easily choose the operating system you want along with any specific types of versions or bootable flags.”

Prerequisuites:

  1. Docker. I am running docker on a dedicated VM called boot.zion
  2. Esxi installer iso from the vmware website.
  3. Dhcp server that supports advanced dhcp options.

The netbootxyz docker image comes with:

  • nginx: webserver
  • tftp-hpa: tftp server

Step A: Use docker to run the netbootxyz container from linuxserver.io

cat compose.yaml 
services:
  netbootxyz:
    image: lscr.io/linuxserver/netbootxyz:latest
    container_name: netbootxyz
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Etc/UTC
    volumes:
      - ./config:/config
      - ./assets:/assets
    ports:
      - 3000:3000
      - 69:69/udp
      - 80:80
    restart: unless-stopped

docker compose up -d

  • Port 3000 is used for the web interface to access netbootxyz
  • Port 69(udp) is used for TFTP
  • Port 80 is used to access files over http. Specifically the contents in the assets folder
  • Folder config/ contains files that can be used to tweak the iPXE menu
  • Folder assets/ is used to hold local images that you can boot off from

You should now be able to access the web interface on your docker host machine using http://boot.zion:3000 and view the assets folder using http://boot.zion/. Replace boot.zion with your docker hostname.

Step B: Configure DHCP Options.

As I use opnsense as my router/dhcp server i used the following instructions from here

Got to: Services -> DHCPv4 Under the Advanced “Network Booting” section. * check enable * Next server- IP of docker host * Default BIOS file name- netboot.xyz.kpxe * UEFI 32 bit file name- netboot.xyz.efi * UEFI 64 bit file name- netboot.xyz.efi

Step C: Extract the contents of the esxi installer iso and copy them into the assets folder.

  1. Install 7z. sudo apt install p7zip-full -y
  2. Extract iso. 7z x iso/VMware-VMvisor-Installer-7.0U3n-21930508.x86_64.iso -oassets/vmware/esxi70u3n
  3. Change all files to lower case using the command

for i in $( find . | grep [A-Z] ); do mv -i $i `echo $i | tr 'A-Z' 'a-z'`; done

If you get an error like, mv: cannot stat './efi/BOOT/BOOT.CFG': No such file or directory run the same script again.

Step D: Modify boot.cfg

  1. Remove ‘/’ from the boot.cfg file. sed -i 's/\///g' boot.cfg
  2. Modify boot.cfg and change prefix= to prefix=http://boot.zion/vmware/esxi70u3n
  3. The modified boot.cfg should look like below:
bootstate=0
title=Loading ESXi installer
timeout=5
prefix=http://boot.zion/vmware/esxi70u3n
kernel=b.b00
kernelopt=runweasel cdromBoot
modules=jumpstrt.gz --- useropts.gz --- features.gz --- k.b00 --- uc_intel.b00 --- uc_amd.b00 --- uc_hygon.b00 --- procfs.b00 --- vmx.v00 --- vim.v00 --- tpm.v00 --- sb.v00 --- s.v00 --- atlantic.v00 --- bnxtnet.v00 --- bnxtroce.v00 --- brcmfcoe.v00 --- elxiscsi.v00 --- elxnet.v00 --- i40en.v00 --- iavmd.v00 --- icen.v00 --- igbn.v00 --- ionic_en.v00 --- irdman.v00 --- iser.v00 --- ixgben.v00 --- lpfc.v00 --- lpnic.v00 --- lsi_mr3.v00 --- lsi_msgp.v00 --- lsi_msgp.v01 --- lsi_msgp.v02 --- mtip32xx.v00 --- ne1000.v00 --- nenic.v00 --- nfnic.v00 --- nhpsa.v00 --- nmlx4_co.v00 --- nmlx4_en.v00 --- nmlx4_rd.v00 --- nmlx5_co.v00 --- nmlx5_rd.v00 --- ntg3.v00 --- nvme_pci.v00 --- nvmerdma.v00 --- nvmetcp.v00 --- nvmxnet3.v00 --- nvmxnet3.v01 --- pvscsi.v00 --- qcnic.v00 --- qedentv.v00 --- qedrntv.v00 --- qfle3.v00 --- qfle3f.v00 --- qfle3i.v00 --- qflge.v00 --- rste.v00 --- sfvmk.v00 --- smartpqi.v00 --- vmkata.v00 --- vmkfcoe.v00 --- vmkusb.v00 --- vmw_ahci.v00 --- bmcal.v00 --- crx.v00 --- elx_esx_.v00 --- btldr.v00 --- esx_dvfi.v00 --- esx_ui.v00 --- esxupdt.v00 --- tpmesxup.v00 --- weaselin.v00 --- esxio_co.v00 --- loadesx.v00 --- lsuv2_hp.v00 --- lsuv2_in.v00 --- lsuv2_ls.v00 --- lsuv2_nv.v00 --- lsuv2_oe.v00 --- lsuv2_oe.v01 --- lsuv2_oe.v02 --- lsuv2_sm.v00 --- native_m.v00 --- qlnative.v00 --- trx.v00 --- vdfs.v00 --- vmware_e.v00 --- vsan.v00 --- vsanheal.v00 --- vsanmgmt.v00 --- tools.t00 --- xorg.v00 --- gc.v00 --- imgdb.tgz --- basemisc.tgz --- resvibs.tgz --- imgpayld.tgz
build=7.0.3-0.95.21930508
updated=

Step E: Optional - If you want to automate installation and use a kickstart file

Change prefix= to prefix=http://boot.zion/vmware/esxi70u3n ks=http://boot.zion/vmware/esxi70u3n/kickstart.cfg

Step F: Boot the installer on the target VM/Machine.

  1. After network boot has come up with netbootxyz, choose Linux Network Installs (64-bit)
  2. Select VMware ESXi
  3. Select Base URL [ ]
  4. Enter URL http://boot.zion/vmware/esxi70u3n
  5. Select VMware ESXi Install
  6. Installer boot will start

Step G: Optional. Add a menu item to netboot.xyz

  1. Go to http://boot.zion:3000 and select Menus -> menu.ipxe

2. Add the below to create a sub menu

item --gap VMware
item ESXi70U3N ${space} ESXi70U3N
item ESXi70 ${space} ESXi70
  1. Add the below for the respective boot options. In the below example, i have two asset folders, esxi70u3n and esxi70. I am booting from the relevant folder using the variable “base_url”
:ESXi70U3N
set base_url http://boot.zion/vmware/esxi70u3n
echo ${base_url}
isset ${base_url} && goto boot_check

:ESXi70
set base_url http://boot.zion/vmware/esxi70 && goto boot_check

:boot_check
iseq ${platform} efi && goto efi_boot || goto legacy_boot

:efi_boot
echo ${base_url}
imgfree
kernel ${base_url}/efi/boot/bootx64.efi -c ${base_url}/boot.cfg
goto boot

:legacy_boot
imgfree
kernel ${base_url}/mboot.c32 -c ${base_url}/boot.cfg
goto boot

:boot
md5sum bootx64.efi
boot
goto main_menu

Attached below is a copy of menu.ipxe from my lab.

#!ipxe

:start
isset ${arch} && goto skip_arch_detect ||
cpuid --ext 29 && set arch x86_64 || set arch i386
iseq ${buildarch} arm64 && set arch arm64 ||
:skip_arch_detect
chain --autofree boot.cfg ||
echo Attempting to retrieve latest upstream version number...
chain --timeout 5000 https://boot.netboot.xyz/version.ipxe ||
ntp 0.pool.ntp.org ||
iseq ${cls} serial && goto ignore_cls ||
set cls:hex 1b:5b:4a  # ANSI clear screen sequence - "^[[J"
set cls ${cls:string}
:ignore_cls


isset ${menu} && goto ${menu} ||
isset ${ip} || dhcp

:main_menu
clear menu
set space:hex 20:20
set space ${space:string}
isset ${next-server} && menu ${site_name} v${version} - next-server: ${next-server} || menu ${site_name}
item --gap Default:
item local ${space} Boot from local hdd
item --gap VMware
item ESXi70U3N ${space} ESXi70U3N
item ESXi70 ${space} ESXi70
item --gap Distributions:
iseq ${menu_linux} 1 && item linux ${space} Linux Network Installs (64-bit) ||
iseq ${menu_linux_i386} 1 && item linux-i386 ${space} Linux Network Installs (32-bit) ||
iseq ${menu_linux_arm} 1 && item linux-arm ${space} Linux Network Installs (arm64) ||
iseq ${menu_live} 1 && item live ${space} Live CDs ||
iseq ${menu_live_arm} 1 && item live-arm ${space} Live CDs ||
iseq ${menu_bsd} 1 && item bsd ${space} BSD Installs ||
iseq ${menu_unix} 1 && item unix ${space} Unix Network Installs ||
iseq ${menu_freedos} 1 && item freedos ${space} FreeDOS ||
iseq ${menu_windows} 1 && item windows ${space} Windows ||
item --gap Tools:
iseq ${menu_utils} 1 && iseq ${platform} efi && item utils-efi ${space} Utilities (UEFI) ||
iseq ${menu_utils} 1 && iseq ${platform} pcbios && iseq ${arch} x86_64 && item utils-pcbios-64 ${space} Utilities (64-bit) ||
iseq ${menu_utils} 1 && iseq ${platform} pcbios && iseq ${arch} i386 && item utils-pcbios-32 ${space} Utilities (32-bit) ||
iseq ${menu_utils_arm} 1 && item utils-arm ${space} Utilities (arm64) ||
item change_arch ${space} Architecture: ${arch}
item shell ${space} iPXE shell
item netinfo ${space} Network card info
iseq ${menu_pci} 1 && item lspci ${space} PCI Device List ||
item about ${space} About netboot.xyz
item --gap Signature Checks:
item sig_check ${space} netboot.xyz [ enabled: ${sigs_enabled} ]
isset ${github_user} && item --gap Custom Github Menu: ||
isset ${github_user} && item custom-github ${space} ${github_user}'s Custom Menu ||
isset ${custom_url} && item --gap Custom URL Menu: ||
isset ${custom_url} && item custom-url ${space} Custom URL Menu ||
isset ${menu} && set timeout 0 || set timeout ${boot_timeout}
choose --timeout ${timeout} --default ${menu} menu || goto local
echo ${cls}
goto ${menu} ||
iseq ${sigs_enabled} true && goto verify_sigs || goto change_menu

:verify_sigs
imgverify ${menu}.ipxe ${sigs}${menu}.ipxe.sig || goto error
goto change_menu

:ESXi70U3N
set base_url http://boot.zion/vmware/esxi70u3n
echo ${base_url}
isset ${base_url} && goto boot_check

:ESXi70
set base_url http://boot.zion/vmware/esxi70 && goto boot_check

:boot_check
iseq ${platform} efi && goto efi_boot || goto legacy_boot

:efi_boot
echo ${base_url}
imgfree
kernel ${base_url}/efi/boot/bootx64.efi -c ${base_url}/boot.cfg
goto boot

:legacy_boot
imgfree
kernel ${base_url}/mboot.c32 -c ${base_url}/boot.cfg
goto boot

:boot
md5sum bootx64.efi
boot
goto main_menu

:change_menu
chain ${menu}.ipxe || goto error
goto main_menu

:error
echo Error occured, press any key to return to menu ...
prompt
goto main_menu

:local
echo Booting from local disks ...
exit 1

:shell
echo Type "exit" to return to menu.
set menu main_menu
shell
goto main_menu

:change_arch
iseq ${arch} x86_64 && set arch i386 && set menu_linux_i386 1 && set menu_linux 0 && goto main_menu ||
iseq ${arch} i386 && set arch x86_64 && set menu_linux_i386 0 && set menu_linux 1 && goto main_menu ||
goto main_menu

:sig_check
iseq ${sigs_enabled} true && set sigs_enabled false || set sigs_enabled true
goto main_menu

:about
chain https://boot.netboot.xyz/about.ipxe || chain about.ipxe
goto main_menu

:custom-github
chain https://raw.githubusercontent.com/${github_user}/netboot.xyz-custom/master/custom.ipxe || goto error
goto main_menu

:custom-url
chain ${custom_url}/custom.ipxe || goto error
goto main_menu

:custom-user
chain custom/custom.ipxe
goto main_menu