Discover how to install Windows 10 over PXE Linux server in just a few steps! Don’t miss out—learn the efficient, sysadmin-friendly method to deploy Windows OS remotely like a pro. #centlinux #linux #pxe
Table of Contents
Problem Statement
In our previous posts, we have configured a PXE boot server for automated installation of RHEL 7.5 operating system that supports both BIOS and UEFI based clients. Later on, we added RHEL 6 and Ubuntu 18.10 installation options in our PXE boot server. We have also written Kickstart files for automated installations of both operating systems.
Before concluding our PXE configuration series, we decided to write an article to add the installation option of a Microsoft’s operating system. Therefore, we have selected the Microsoft Windows 10 (the latest OS at the time of this writeup) as the candidate for our PXE boot server.
Linux Server Specification
In this article, we will install Windows 10 over PXE Server.
We use the same Linux server that we have configured as PXE Boot Server in our previous article. These specifications have been re-mentioned below for convenience of the readers.
- CPU – 2 Core (2.4 Mhz)
- Memory – 2 GB
- Storage – 50 GB
- Operating System – RHEL 7.5
- Hostname – pxe-server.itlab.com
- IP Address – 192.168.116.41/24
Furthermore, we need a client system to create Windows PE iso file. Here, we are using a MS Windows 8.1 machine for this purpose, however, you can use other versions of MS Windows as well (be noted that configurations are slightly different in MS Windows versions prior to 8.1).
Recommended Training: The Linux Command Line Bootcamp: Beginner To Power User from Colt Steele
Read Also: How to install Linux over PXE Server
Install Samba Server on Linux
We used Samba server to share installation media of MS Windows 10 Operating System with PXE clients.
Connect to pxe-server.itlab.com using ssh and install Samba server.
yum install -y samba
Create a directory to share Windows 10 installation media.
mkdir /smbshare
Adjust SELinux permissions.
semanage fcontext -a '/smbshare(/.*)?' -t samba_share_t
restorecon -Rv /smbshare
Create an user to control access to Samba share.
useradd -s /sbin/nologin ahmer
smbpasswd -a ahmer
Change owner of the shared directory with user ahmer.
chown ahmer:ahmer /smbshare/
Allow Samba service in Linux Firewall.
firewall-cmd --permanent --add-service=samba
firewall-cmd --reload
Add directives in /etc/samba/smb.conf to share the directory /smbshare with clients.
cat >> /etc/samba/smb.conf << EOF
[install]
comment = Installation Media
path = /smbshare
public = yes
writable = no
printable = no
browseable = yes
EOF
Start and enable Samba services.
systemctl start smb nmb
systemctl enable smb nmb
Attach MS Windows 10 ISO/DVD and mount it at /mnt/iso (you can use any mountpoint, according to your choice).
mount -t iso9660 /dev/cdrom /mnt/iso
Output:
mount: /dev/sr0 is write-protected, mounting read-only
Copy contents of /mnt/iso to /smbshare/windows10 directory.
cp -rf /mnt/iso /smbshare/windows10
Create a Customized Windows PE ISO
Connect to MS Windows 8.1 client.
We required Windows ADK (Assessment and Deployment Kit) to create our Windows PE iso. Therefore, we have downloaded it from Microsoft’s website and installed it on our Windows 8.1 client.
After installation, run Deployment and Imaging Tools Environment as Administrator.
Run the following command to create working directories for Win PE. (Command output has been trimmed to show only success status)
cd C:\Program Files (x86)\Windows Kits\8.0\Assessment and Deployment Kit\Windows Preinstallation Environment
copype x86 c:\WinPE_x86\
Output:
---
186 File(s) copied
1 file(s) copied.
1 file(s) copied.
1 file(s) copied.Success
We will customize the startup script startcmd.net, so MS Windows 10 setup will start automatically. Therefore, mount the image file and customize it accordingly.
Dism /Mount-Image /ImageFile:"c:\WinPE_x86\media\sources\boot.wim" /index:1 /MountDir:"c:\WinPE_x86\mount"
Output:
Deployment Image Servicing and Management tool
Version: 6.2.9200.16384Mounting image
[==========================100.0%==========================]
The operation completed successfully.
Now, edit startnet.cmd.
notepad c:\WinPE_x86\mount\Windows\System32\Start\net.cmd
And add following lines therein.
wpeinit
net use z: \\192.168.116.41\install\windows10 /user:ahmer@123
z:\setup.exe
Save and unmount the image file.
Dism /Unmount-Image /MountDir:"c:\WinPE_x86\mount" /commit
Output:
Deployment Image Servicing and Management tool
Version: 6.2.9200.16384Saving image
[==========================100.0%==========================]
Unmounting image
[==========================100.0%==========================]
The operation completed successfully.
Generate winpe.iso file.
MakeWinPEMedia /ISO c:\WinPE_x86 c:\winpe.iso
Output:
Creating c:\winpe.iso...100% complete
Success
Transfer winpe.iso file to pxe-server-itlab.com and copy it to /var/lib/tftpboot/networkboot/windows10/
directory.
Now, connect to pxe-server.itlab.com again using ssh.
Copy Kernel boot image for MS Windows in tftpboot directory.
cp /usr/lib/syslinux/memdisk /var/lib/tftpboot/
Edit PXE boot menu for BIOS based clients.
vi /var/lib/tftpboot/pxelinux.cfg/default
And add following menu option at the end of file.
label Install MS Windows 10
menu label Install MS Windows 10
kernel memdisk
initrd /networkboot/windows10/winpe.iso
append iso raw
Connect a new client to your network and turn it on. It should get the IP address from DHCP server and display our PXE boot Menu as follows:
Select Install MS Windows 10 option and press <ENTER>.
We have successfully added the MS Windows 10 installation option in our Linux based PXE server.
Frequently Asked Questions (FAQs)
What is PXE booting, and how does it help install Windows 10?
PXE (Preboot Execution Environment) allows a computer to boot and install an operating system over a network instead of using physical media. A Linux server can host the Windows 10 installation files for remote deployment.
Do I need a Windows server to set up PXE for Windows 10?
No, you can use a Linux server (like Ubuntu or CentOS) to configure a PXE boot environment for Windows 10 installation with the right tools and configurations.
What files are required on the Linux server for Windows 10 PXE installation?
You’ll need the Windows 10 ISO (or extracted files), a TFTP server to host boot files, a DHCP server to assign IPs, and a network share (SMB/NFS) for the Windows installation files.
Can I use PXE to install Windows 10 on multiple computers at once?
Yes! Once the PXE server is set up, any device connected to the same network can boot and install Windows 10 simultaneously (depending on network bandwidth and server capacity).
Are there any risks when installing Windows 10 over PXE?
If configured incorrectly, you might face boot failures or network issues. Ensure proper DHCP settings, correct file paths, and compatible hardware (PXE-supported network cards). Always back up data before installation.
Final Thoughts
By now, you’ve seen how straightforward it can be to install Windows 10 over a PXE Linux server. From setting up the TFTP and DHCP services to booting the Windows installer remotely, you’re just steps away from streamlining your deployment process.
If you’re still manually installing operating systems or juggling bootable USBs, you’re already falling behind. Modern sysadmins automate and scale—and PXE booting is at the heart of that transformation.
Don’t let outdated methods slow you down. Get ahead, save time, and take control of your infrastructure.
✅ Ready to level up your deployment game? Start implementing PXE boot with Linux today—your future self will thank you.
Struggling with Linux server management? I offer professional support to ensure your servers are secure, optimized, and always available. Visit my Fiverr profile to learn more!
Looking for something?
Latest Articles
Preboot eXecution Environment (PXE) is a widely used protocol that enables a workstation to boot from the network rather than from a local hard drive. This is particularly useful in large-scale deployments where installing operating systems on multiple machines manually would be impractical. Configuring a PXE boot server on Ubuntu 20.04 for Windows deployment can streamline the process of setting up new computers in your network. This article walks through the necessary steps to set up a PXE Boot Server for Windows deployment.
Prerequisites
- A machine running Ubuntu 20.04
- Access to the internet
- Windows installation media
- Sufficient storage for the Windows installation files
- Basic knowledge of Linux command line interface
Step 1: Installing Required Packages
First, we need to install the necessary software on the Ubuntu server. This includes a DHCP server, a TFTP server, and an NFS server.
sudo apt update
sudo apt install -y isc-dhcp-server tftpd-hpa nfs-kernel-server
Step 2: Configuring the DHCP Server
The DHCP server assigns IP addresses to clients in the network. Edit the DHCP configuration file to define the range of IP addresses and specify the PXE boot filename.
sudo nano /etc/dhcp/dhcpd.conf
Add the following configuration, adjusting the IP address range to fit your network:
subnet 192.168.1.0 netmask 255.255.255.0 {
range 192.168.1.20 192.168.1.30;
option domain-name-servers ns1.example.org, ns2.example.org;
option domain-name "example.org";
option routers 192.168.1.1;
option broadcast-address 192.168.1.255;
default-lease-time 600;
max-lease-time 7200;
next-server 192.168.1.10;
filename "pxelinux.0";
}
Step 3: Setting Up the TFTP Server
The TFTP server hosts the boot files needed by the PXE client. Configure the TFTP server by editing the default file.
sudo nano /etc/default/tftpd-hpa
Ensure the following lines are set in the file:
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/var/lib/tftpboot"
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="--secure"
Step 4: Configuring the NFS Server
The NFS server will host the Windows installation files, allowing PXE clients to access them. Create a directory to store the files and add it to the NFS exports.
sudo mkdir -p /srv/nfs/win-install
sudo nano /etc/exports
Add the following line, granting access to your network:
/srv/nfs/win-install 192.168.1.0/24(ro,sync,no_subtree_check)
Export the shared directory and restart the NFS services:
sudo exportfs -a
sudo systemctl restart nfs-kernel-server
Step 5: Preparing the Windows Installation Files
Mount the Windows installation media and copy the contents to the NFS share.
sudo mount /dev/cdrom /mnt
sudo cp -r /mnt/* /srv/nfs/win-install/
sudo umount /mnt
Step 6: Configuring PXE Boot Menus
Set up the PXE boot menus to allow clients to choose the Windows installation. Download and configure the PXELINUX bootloader.
sudo apt install -y syslinux
sudo cp /usr/lib/syslinux/modules/bios/pxelinux.0 /var/lib/tftpboot/
sudo mkdir /var/lib/tftpboot/pxelinux.cfg
sudo nano /var/lib/tftpboot/pxelinux.cfg/default
Add the following to the default file:
DEFAULT menu.c32
PROMPT 0
MENU TITLE PXE Boot Menu
LABEL windows
MENU LABEL Windows Install
KERNEL pxeboot.n12
APPEND -
Conclusion
Setting up a PXE Boot Server for Windows deployment on Ubuntu 20.04 involves several steps but can significantly simplify the installation of Windows on multiple computers across a network. By following this guide, you should be able to boot a client machine from the network and proceed with a Windows installation.
If you need professional assistance to manage and optimize your PXE boot server or other DevOps tasks, consider the option to hire remote DevOps engineers to support your infrastructure needs.
Прочитано: 7 666
Итак, я плавно со своим PXE сервером развернутым на Ubuntu 18.04 Server подошел к этапу, когда нужно в дополнении к автоматизированным установкам Ubuntu 18.04 Desktop & Server дополнить функционал. Уже сделал, что можно загрузившись через LiveCD инструмент сбросить Windows пароль, проверить жесткий диск на предмет заражения вирусами т. д. А вот моя мечта это возможность устанавливать Windows 7,10 и серверную линейку из среды Ubuntu. Использовать WDS, пожалуй нет. Я специализируюсь на Ubuntu системах, а значит и пишу и делаю исходя из своего предпочтения. В этой заметке я для себя разобрал, как сделать Windows загрузку из под Ubuntu 18.04 PXE.
Подготавливаю систему Ubuntu 18.04 Server amd64 под первоначальное использование:
ekzorchik@srv-bionic:~$ hg clone --insecure https://172.33.33.7/hg/dproject
ekzorchik@srv-bionic:~$ sudo ./dproject/default
Устанавливаю пакет Samba на нем, точнее с его помощью у меня будет файловый ресурс где будет храниться образа Windows 7, как x86/amd64:
ekzorchik@srv-bionic:~$ sudo apt-get install samba samba-common -y
ekzorchik@srv-bionic:~$ sudo mv /etc/samba/smb.conf /etc/samba/smb.conf.backup
ekzorchik@srv-bionic:~$ sudo nano /etc/samba/smb.conf
[global]
workgroup = WORKGROUP
server string = Samba Server Version %v
log file = /var/log/samba/log.%m
max log size = 50
idmap config * : backend = tdb
cups options = raw
netbios name = pxe
map to guest = bad user
dns proxy = no
public = yes
## For multiple installations the same time - not lock kernel
kernel oplocks = no
nt acl support = no
security = user
guest account = nobody
[install]
comment = Windows 7 Image
path = /windows
read only = no
browseable = yes
public = yes
printable = no
guest ok = yes
oplocks = no
level2 oplocks = no
locking = no
acl allow execute always = true
На заметку: Параметр “acl allow execute always = true
” в smb.conf
необходим для возможности запускать на исполнение файлы exe.
ekzorchik@srv-bionic:~$ sudo testparm
Load smb config files from /etc/samba/smb.conf
rlimit_max: increasing
rlimit_max (1024) to minimum Windows limit (16384)
ekzorchik@srv-bionic:~$ sudo mkdir -p /windows/x32
ekzorchik@srv-bionic:~$ sudo mkdir -p /windows/x64
Монтирую ISO образ Windows 7 на каталог Samba сервиса:
ekzorchik@navy:~$ scp /media/ekzorchik/iso/SW_DVD5_Win_Pro_7w_SP1_64BIT_Russian_-2_MLF_X17-59431.ISO ekzorchik@172.33.33.25:/home/ekzorchik
ekzorchik@srv-bionic:~$ sudo mount -o loop SW_DVD5_Win_Pro_7w_SP1_64BIT_Russian_-2_MLF_X17-59431.ISO /mnt
mount: /mnt: WARNING: device write-protected, mounted read-only.
ekzorchik@srv-bionic:~$ sudo cp -fr /mnt/* /windows/x64/
ekzorchik@srv-bionic:~$ sudo umount /mnt
ekzorchik@navy:~$ scp /media/ekzorchik/iso/SW_DVD5_Win_Pro_7w_SP1_32BIT_Russian_MLF_X17-28979.ISO ekzorchik@172.33.33.25:/home/ekzorchik
ekzorchik@srv-bionic:~$ sudo mount -o loop SW_DVD5_Win_Pro_7w_SP1_32BIT_Russian_MLF_X17-28979.ISO /mnt
ekzorchik@srv-bionic:~$ sudo cp -fr /mnt/* /windows/x32/
ekzorchik@srv-bionic:~$ sudo umount /mnt
ekzorchik@srv-bionic:~$ sudo chmod -R 755 /windows/
ekzorchik@srv-bionic:~$ sudo chown -R nobody:nogroup /windows/
ekzorchik@srv-bionic:~$ sudo /etc/init.d/smbd restart
ekzorchik@srv-bionic:~$ sudo /etc/init.d/nmbd restart
Устанавливаю PXE сервис:
Устанавливаю в систему пакет tftpd-hpa:
ekzorchik@srv-bionic:~$ sudo apt-get install tftpd-hpa -y
ekzorchik@srv-bionic:~$ sudo mkdir /media/pxeboot
ekzorchik@srv-bionic:~$ sudo chmod -R 777 /media/pxeboot/
ekzorchik@srv-bionic:~$ sudo chown -R nobody:nogroup /media/pxeboot/
ekzorchik@srv-bionic:~$ sudo cp /etc/default/tftpd-hpa /etc/default/tftpd-hpa.backup
ekzorchik@srv-bionic:~$ sudo nano /etc/default/tftpd-hpa
# /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/media/pxeboot"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure --create --verbose"
Перезапускаю сервис tftpd-hpa:
ekzorchik@srv-bionic:~$ sudo service tftpd-hpa restart && sudo service tftpd-hpa status | head -n 5
ekzorchik@srv-bionic:~$ sudo apt-get install pxelinux syslinux -y
ekzorchik@srv-bionic:~$ sudo cp /usr/lib/syslinux/modules/bios/vesamenu.c32 /media/pxeboot/
ekzorchik@srv-bionic:~$ sudo cp /usr/lib/syslinux/modules/bios/libcom32.c32 /media/pxeboot/
ekzorchik@srv-bionic:~$ sudo cp /usr/lib/syslinux/modules/bios/libutil.c32 /media/pxeboot/
ekzorchik@srv-bionic:~$ sudo cp /usr/lib/PXELINUX/pxelinux.0 /media/pxeboot/
ekzorchik@srv-bionic:~$ sudo cp /usr/lib/syslinux/memdisk /media/pxeboot/
ekzorchik@srv-bionic:~$ sudo cp /usr/lib/syslinux/modules/bios/ldlinux.c32 /media/pxeboot/
ekzorchik@srv-bionic:~$ sudo mkdir /media/pxeboot/pxelinux.cfg/
ekzorchik@srv-bionic:~$ sudo nano /media/pxeboot/pxelinux.cfg/default
#Секция по умолчанию, если пользователь не выбрал никакого варианта или нажал
клавишу Enter
DEFAULT vesamenu.c32
#Секция времени в секундах, в течении которого отображается загрузочное меню
TIMEOUT 600
#Секция отображения строки для выбора варианта загрузки (1 - view, 0 - hidden)
PROMPT 0
MENU TITLE PXE MENU
# загрузка системы с диска
LABEL Boot from first hard disk
localboot 0x80
label Install Windows 7 x32/x64
KERNEL memdisk
INITRD windows/winpe_amd64.iso
APPEND iso raw
ekzorchik@srv-bionic:~$ sudo chown -R nobody:nogroup /media/pxeboot/
ekzorchik@srv-bionic:~$ ss -l4 | grep tftp
udp UNCONN 3072 0 0.0.0.0:tftp 0.0.0.0:*
Далее на VM под управлением Windows 7 x64 устанавливаю пакет Windows AIK (KB3AIK_RU.iso
размером 1.2 Gb
) и через инструмент Deployment Tools Command Prompt
Win + Все программы — Microsoft Windows AIK – «Командная стройка средств развертывания» (запускаю через правый клик с правами администратора)
(для amd64 создаю winpe образ)
C:\Program Files\Windows AIK\Tools\PETools>copype amd64 c:\winPE_x64
c:\winPE_x64>copy "c:\Program Files\Windows AIK\Tools\PETools\amd64\winpe.wim" c:\winPE_x64\ISO\sources\Boot.wim
c:\winPE_x64>copy "c:\Program Files\Windows AIK\Tools\amd64\imagex.exe" c:\winPE_x64\ISO\
c:\winPE_x64>oscdimg -n -bC:\winPE_x64\etfsboot.com c:\winPE_x64\ISO c:\winPE_x64\winpe_amd64.iso
Computing directory information complete
Image file is 170760192 bytes
Writing 22 files in 9 directories to c:\winPE_x64\winpe_amd64.iso
100% complete
Final image file is 170760192 bytes
Done.
(для x86 создаю winpe образ)
C:\Program Files\Windows AIK\Tools\PETools>copype x86 c:\winPE_x86
c:\winPE_x86>copy "c:\Program Files\Windows AIK\Tools\PETools\x86\winpe.wim" c:\winPE_x86\ISO\sources\Boot.wim
c:\winPE_x86>copy "c:\Program Files\Windows AIK\Tools\x86\imagex.exe" c:\winPE_x86\ISO\
c:\winPE_x86>oscdimg -n -bC:\winPE_x86\etfsboot.com c:\winPE_x86\ISO c:\winPE_x86\winpe_x86.iso
Computing directory information complete
Image file is 145268736 bytes
Writing 18 files in 8 directories to c:\winPE_x86\winpe_x86.iso
100% complete
Final image file is 145268736 bytes
Done.
Копирую получившийся iso файл на SAMBA сервис в каталог \\172.33.33.25\install
ekzorchik@srv-bionic:~$ sudo mkdir /media/pxeboot/windows/
ekzorchik@srv-bionic:~$ sudo mv /windows/winpe_amd64.iso /media/pxeboot/windows/
ekzorchik@srv-bionic:~$ sudo mv /windows/winpe_x86.iso /media/pxeboot/windows/
Теперь беру VM и выставляю ей что она сперва должна произвести загрузку по сети
Вижу запрос на нажатие клавиши нажимаю клавишу Enter и производится загрузка с диска, а диск у меня это iso образ. Тем самым стартует загрузка Windows, но не привычная установка а приглашение командной строки:
В этом приглашении следует уже подмонтировать SAMBA ресурс и запустить установку ОС, т. е. Windows 7 Pro x64
X:\windows\system32> net use M: \\172.33.33.25\install\x64
где M:
– это произвольное буква на которую монтирую доступ к ресурсу.
Затем перехожу на букву M:
и запускаю файл инсталляции операционной системы:
X:\windows\system32\cd /d M:
M:\>setup.exe
Указываю метод ввода (я предпочитаю чтобы дефолтным был английский язык, а не русский)
Нажимаю «Далее» – «Установить» и начинается обыденный процесс инсталляции операционной системы Windows, как если бы я просто записал образ на болванку или USB Flash Drive, выставил в BIOS с чего загружаться и загрузился.
Вообще следует разделить установку Windows в default файле:
LABEL Boot from first hard disk
localboot 0x80
LABEL Install Windows 7 Pro SP1 x64
KERNEL memdisk
INITRD windows/winpe_amd64.iso
APPEND iso raw
LABEL Install Windows 7 Pro SP1 x86
KERNEL memdisk
INITRD windows/winpe_x86.iso
APPEND iso raw
Далее, для того, чтобы автоматизировать установку системы, можно с помощью “диспетчера образов системы Windows” подготовить файл ответов Autounattend.xml
(именно так должен именовать файл ответов), и запускать установку с файлом ответов:
M:\setup.exe /unattend: Autounattend.xml
Исходя из этого, становится возможным на большом количестве персональных компьютеров которые необходимо вот прям, завтра, или сейчас ввести в эксплуатацию установить ось задействовав текущий PXE сервис и общий ресурс в роли SAMBA сервиса на который взгромоздить подготовленный ранее файл ответом или ставить все в ручную.
О том, как подготовить файл ответов будет в следующей заметке.
А пока я прощаюсь, на этом всё, с уважением автор блога Олло Александр aka ekzorchik
When installing Windows 10,
It was troublesome to make a USB memory and prepare a CD drive, so I installed it using PXE.
Technology to use
First, I will briefly introduce the technical elements used this time.
PXE
** PXE (Preboot eXecution Environment) ** is a mechanism for clients to acquire software from the network and execute it.
This PXE client function is implemented in common NICs and can be used for OS installation, etc. Wikipedia
iPXE
iPXE is an open source network boot firmware.
In addition to the PXE client function, some functions have been added.
For details, see This page.
The important thing to use this time is that you can get the software via HTTP and start it.
WinPE
** WinPE (Windows PE) ** is a small OS that can be used for deploying Windows OS.
Functions are limited compared to normal Windows.
Installation overview
This time, we will combine these technologies to install the Windows OS.
In the end, it looks like this.
Prepare one Linux server for installation.
This time, it is assumed that the new personal computer and the Linux server are connected by L2, and other hosts are not connected.
First, we will create a Linux server.
Linux server construction
This time, I prepared a VM of ** Fedora 33 WorkStation **.
[testing@localhost ~]$ cat /etc/fedora-release
Fedora release 33 (Thirty Three)
Set the interface to the static IP address ** 192.168.0.1/24 **.
(Internet connection is required to install various software, so it may be necessary to switch back to DHCP depending on the environment.)
[testing@localhost ~]$ ip a sh ens3
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:54:00:28:c1:a9 brd ff:ff:ff:ff:ff:ff
altname enp0s3
inet 192.168.0.1/24 brd 192.168.0.255 scope global noprefixroute ens3
valid_lft forever preferred_lft forever
inet6 fe80::da34:251a:af7d:962d/64 scope link noprefixroute
valid_lft forever preferred_lft forever
1. DHCP server, TFTP server settings
First, set up a DHCP server and a TFTP server.
The dnsmasq used this time has a TFTP server function, but it is not used.
Install tftpd separately.
DHCP
#It was included in dnsmasq when Fedora was installed.
$ rpm -q dnsmasq
dnsmasq-2.82-3.fc33.x86_64
$ sudo vi /etc/dnsmasq.conf
<Described below>
$ sudo firewall-cmd --add-service dhcp
$ sudo systemctl start dnsmasq
/etc/dnsmasq.conf (excerpt)
#Change the interface to the required interface.
interface=ens3
#Appropriate dhcp-Comment out the range.
dhcp-range=192.168.0.50,192.168.0.150,12h
#Comment out the settings related to iPXE.
#3rd line, dhcp returned to iPXE-Change boot to the URL of the script. The script will be created later.
dhcp-boot=undionly.kpxe
dhcp-match=set:ipxe,175 # iPXE sends a 175 option.
dhcp-boot=tag:ipxe,http://192.168.0.1/boot.ipxe
TFTP
#This time, get iPXE from Fedora Repository.
$ sudo dnf install ipxe-bootimgs
$ rpm -ql ipxe-bootimgs
/usr/share/doc/ipxe-bootimgs
/usr/share/doc/ipxe-bootimgs/COPYING
/usr/share/doc/ipxe-bootimgs/COPYING.GPLv2
/usr/share/doc/ipxe-bootimgs/COPYING.UBDL
/usr/share/ipxe
/usr/share/ipxe/ipxe-i386.efi
/usr/share/ipxe/ipxe-x86_64.efi
/usr/share/ipxe/ipxe.dsk
/usr/share/ipxe/ipxe.iso
/usr/share/ipxe/ipxe.lkrn
/usr/share/ipxe/ipxe.usb
/usr/share/ipxe/undionly.kpxe # <-Use this.
$ sudo dnf install tftp-server
$ sudo firewall-cmd --add-service tftp
$ sudo systemctl start tftp
$ sudo cp /usr/share/ipxe/undionly.kpxe /var/lib/tftpboot/
If you do so far, you should be able to confirm until iPXE starts.
2. WinPE preparation
Now create WinPE.
There is a way to create it on Windows, but this time I will try to create it on Linux using ** wimlib **.
At this point, you need a disk image (ISO file) for installing Windows, so download it from Microsoft official and place it on your Linux server.
#For the English version.
$ ls Win10_20H2_v2_English_x64.iso
Win10_20H2_v2_English_x64.iso
WinPE
# wimlib-Install utils and syslinux.
$ sudo dnf install wimlib-utils
$ sudo dnf install syslinux
#Specify the command when starting WinPE.
$ vi boot.cmd
<Described below>
#Mount the installer appropriately.
$ sudo mount Win10_20H2_v2_English_x64.iso /mnt
$ mkwinpeimg -W /mnt -a amd64 -s boot.cmd winpe.img
#Unmount it.
$ sudo umount /mnt
The boot.cmd
file created above.
** Username (testing in the example) is a Linux username,
The password (password in the example) will be the Samba password that you will specify later. ** **
boot.cmd
wpeinit
ping -n 10 localhost
net use n: \\192.168.0.1\testing password /user:testing
n:\installer\setup.exe
You have now created an image of WinPE.
By the way
It may be puzzling to have ping -n 10 localhost
, but it is intended for sleep 10
.
This is a painstaking measure because an error will occur if the network settings are not completed when executing net use
…
3. HTTP server settings
This time I will use Nginx.
HTTP
$ sudo dnf install nginx
$ sudo firewall-cmd --add-service http
$ sudo systemctl start nginx
Place the files needed to start WinPE.
Suddenly, I use software called wimboot.
wimboot
$ wget http://git.ipxe.org/releases/wimboot/wimboot-latest.zip
$ unzip wimboot-latest.zip
$ sudo cp <Extracted directory>/wimboot /usr/share/nginx/html/
Extract the required files from the WinPE image and place them.
WinPE
$ sudo mount winpe.img /mnt/
$ sudo cp -r /mnt/boot /mnt/sources /usr/share/nginx/html
Finally, create an iPXE script file.
(I referred to the wimboot page.)
iPXE script
#Match the file name returned by DHCP.
$ sudo vi /usr/share/nginx/html/boot.ipxe
boot.ipxe
#!ipxe
kernel wimboot
initrd boot/bcd BCD
initrd boot/boot.sdi boot.sdi
initrd sources/boot.wim boot.wim
boot
Hopefully you can see WinPE booting at this point.
4. Samba settings
Next, set up Samba.
Samba
$ sudo dnf install samba
#Set a password. Make it the same as the user name and password described when creating WinPE.
$ sudo smbpasswd -a -U testing
New SMB password:
Retype new SMB password:
Added user testing.
$ sudo firewall-cmd --add-service samba
$ sudo systemctl start smb
Makes it possible to refer to the windows installer.
This time we will use the home directory.
Samba
$ sudo setsebool -P samba_enable_home_dirs=on
$ cd
#Match the path described when creating WinPE.
$ mkdir installer
$ sudo mount -o context=user_u:object_r:user_home_t:s0 Win10_20H2_v2_English_x64.iso installer/
$ ls installer/
autorun.inf boot bootmgr bootmgr.efi efi setup.exe sources
This completes the Linux server settings.
Installation execution
I will install it.
After connecting properly, start a new computer and wait for the installer to start.
After the installer starts, it’s almost as usual.
(If necessary, change the Boot Order at boot time.)
To give an example, let’s try it on a virtual machine.
—First, iPXE boots from PXE.
(This virtual machine does not have this because iPXE starts from the beginning.)
—Next, iPXE gets the script over HTTP and executes it.
—WinPE will start.
—WinPE connects to Samba and launches the installer.
――The rest is the same as usual. (It seems that some options cannot be selected.)
The installation was completed like this.
Impressions
I think it was faster to buy a cheap USB memory because it was so clogged.
However, it is still fun to start the actual machine with PXE.
Version information etc.
The main software versions used this time are listed.
version
$ rpm -q dnsmasq
dnsmasq-2.82-3.fc33.x86_64
$ rpm -q tftp-server
tftp-server-5.2-31.fc33.x86_64
$ rpm -q nginx
nginx-1.18.0-3.fc33.x86_64
$ rpm -q samba
samba-4.13.3-0.fc33.x86_64
$ rpm -q ipxe-bootimgs
ipxe-bootimgs-20200823-1.git4bd064de.fc33.noarch
$ rpm -q wimlib-utils
wimlib-utils-1.13.3-1.fc33.x86_64
Inspired by recent a recent video on the basics of PXE booting by ‘Tall Paul Tech’ (formerly known as CWNE88), as well as a comment by Linus of Linus Tech Tips that his new home server could ’network boot everything in his house’, I wondered how easy it would be to network boot everything in my house. In an ideal world, this would solve a lot of problems regarding managing backups of the drives — by simply not having drives at any client, they can all be managed and backed up centrally by the server. I’ve already come to love the flexiblity in separating storage from compute in a virtualization environment, where I can use a ZFS backend to deal with snapshots, replication, and file integrity checking, and I’ve upgraded the network speed for my workstation so I can work on files directly off the network using mapped network drives, but this still leaves my primary boot disk vulnerable, and while my files are safe, my installed programs are not. I could certainly recover from a boot disk failure without data loss, but it would take a while to get everything re-installed again. So, if I could keep the boot disk on the storage backend, it would gain the integrity protection and snapshots that the data has. Being that Linus will only be concerned with gaming, it’s probably important to be able to boot Windows, as unfortunate as that sounds going in to this project.
The Basic Design⌗
In short, we are going to rely on PXE to get the system off the ground, and then load iPXE (a better PXE) using the ‘chainloading’ method (where traditional PXE is used to load the improved open-source PXE), which we will then use to boot off an iSCSI target containing a Windows disk. If we had Linux clients, we could rely on NFS instead of iSCSI for the root filesystem, and we would curse a whole lot less.
So, there are a few components needed here
- The DHCP server, which at a minimum has to point to the PXE server (the ’next-server’ directive as well as the boot file directive).
- The TFTP server, which stores the files used by PXE to load iPXE
- The HTTP server, which stores additional files used by iPXE to load the operating system, although not required after the bootable iSCSI drive is configured. This could also be expanded to serve custom iPXE scripts to each client based on their MAC address or other unique identifiers.
- The Samba server, which stores the Windows installation media, not required after installation
- The iSCSI target, which stores the virtual block device used to store the Windows C drive
To implement this project, for testing, I’ve created an Ubuntu 20.04 VM on Minilab to act as the server for TFTP + HTTP + Samba + iSCSI, and I have an old amd64 machine which will act as the client. It physically has no hard drive installed. It’s an old Atom processor in an ITX embedded motherboard, but it should be enough to prove that this method works without buying any hardware.
Setting Up the DHCP server⌗
This one is pretty simple. I use OPNsense as my DHCP sever for my existing network, so I just had to go down to Network Booting, Enable Network Booting, and set next-server IP and boot file name. I set the DHCPv4 and DHCPv6 server to have a static reservation for the Ubuntu VM, so it will have a consistent IP without setting a static IP inside the VM, and set next-server to that IP. So, clients will now look to the Ubuntu server for their PXE boot files. I just need enough configuration in the DHCP server to get the dumb PXE clients to load iPXE which is compiled with an embedded script pointing it to the HTTP server, where it can go for the rest of its configuration.
What happens at this point? If I power on the test system with next-server set, and it tries, but there is no TFTP server for it to find. In this case, we want to boot iPXE, which is a better PXE environment than the default, and will hopefully let us boot from iSCSI.
Setting Up the TFTP server⌗
This one is also pretty simple. In my Ubuntu VM, I installed it from apt and that was that.
sudo apt-get install tftpd-hpa
By default it places the data at /srv/tftp, but the config file is located at /etc/default/tftpd-hpa if you’d like to change that. I’m fine with the default location.
I added a blank file called undionly.kpxe to the tftp folder and set that as the boot file name for BIOS in the DHCP settings (the name will become relevant later). I booted my test system (which is old enough to use legacy BIOS) and it attempted to load the file but couldn’t boot it, since it’s just a blank file.
Setting Up the iSCSI Target⌗
If you’re doing this for a ‘production’ environment, you should use something like TrueNAS and setup your iSCSI target there. I’m not an iSCSI expert by any means, but I got this to work. The whole point of this project is to try new things.
First we need to install the iscsi target on Ubuntu:
sudo apt-get install tgt
Then we need to setup an iSCSI target.
sudo nano /etc/tgt/conf.d/target01.conf
And the contents:
<target iqn.2021-09.net.apalrd:win10>
backing-store /srv/disks/win10.img
</target>
And we need a blank image file:
fallocate -l 20G /srv/disks/win10.img
Restart the server
sudo systemctl restart tgt
Look at the status
sudo tgtadm --mode target --op show
So now we have an iSCSI target which we can use to store the Windows C drive, but nothing is installed on the disk image.
Setting up iPXE⌗
We would like to use iPXE to get the process rolling. So, cd to a place you can keep a git repo, and clone it:
git clone git://git.ipxe.org/ipxe.git
Follow the instructions here to make sure you have the dependencies installed so you can build
Then we need to create an embedded file so it will run a script when it boots:
cd ipxe/src
nano boot.ipxe
And the contents:
#!ipxe
#Setup networking
echo Setup Networking
dhcp
#Boot from SAN
echo Boot from SAN
sanboot iscsi:<IP>:::<LUN>:iqn.2021-09.net.apalrd:win10
Although all of the examples for ipxe show LUN as blank (iscsi::::iqn…), I found that I needed to set LUN to 1 with the default configuration of tgt. It seems like LUN 0 is always a Controller, with LUN 1 being the Disk. Not sure why exactly this is, but tgtadm shows the LUN numbers for each target correctly. Additionally, I had trouble with Windows giving me an ‘INACCESSIBLE BOOT DEVICE’ error, which I’ll get to in a bit.
Then we can build ipxe with our embedded script:
make bin/undionly.kpxe EMBED=boot.ipxe
Then finally we copy that into our tftp folder so clients can find it
cp ./bin/undionly.kpxe /src/tftp
Setting up Apache2⌗
Since TFTP is pretty ugly, I installed Apache2 to serve files over HTTP for the installation process. This isn’t used after install, but it could be used along with PHP / some scripts to send more complex commands to iPXE once it’s been loaded via TFTP. There are better guides than mine out there, so just setup a basic apache2 with the http root in /srv/html with full read access to anyone.
Setting up Samba⌗
Again, we need Samba for the install process so I installed it with no permissions and mounting a share ‘server’ at location /srv. This also lets me copy files to the http root, tftp root, and iscsi disks folder from my Windows workstation as needed.
sudo apt-get install samba
sudo nano /etc/samba/smb.conf
Add the following lines to the end:
[server]
comment = Server directory
path = /srv
browseable = yes
guest ok = yes
read only = no
create mask = 0777
acl allow execute always = True
Then restart Samba
sudo systemctl restart smbd nmbd
Installing Windows⌗
I first tried to install Windows in a VM, then copy that VM to the win10.img disk that’s being shared over iSCSI. It was not properly configured for iSCSI and gave me the ‘INACCESSABLE BOOT DEVICE’ error. So, I decided to install it over the network.
I chose to go with the ‘wimboot’ method, booting WinPE (windows preboot execution environment) according to this guide. I modified my boot.ipxe temporarily while installing, created the folder structure using copype, and moved it to my HTTPS directory. Since I was embedding the script instead of loading it from HTTP, I modified all of the paths to include http://<IP>/ in front of them. The resulting script is:
#!ipxe
#Start networking
echo Start Networking
dhcp
#Set web root
set webroot http://<IP>
echo Webroot is ${webroot}
#Set architecture
cpuid --ext 29 && set arch amd64 || set arch x86
echo ARCH is ${arch}
#Sanhook the Windows drive
echo Attaching iSCSI drive
sanhook iscsi:<IP>:::1:iqn.2012-09.net.apalrd:win10
#Load wimboot
echo Loading Wimboot
kernel ${webroot}/wimboot
initrd ${webroot}/${arch}/media/Boot/BCD BCD
initrd ${webroot}/${arch}/media/Boot/boot.sdi boot.sdi
initrd ${webroot}/${arch}/media/sources/boot.wim boot.wim
boot
Of course, you need to make ipxe again and copy the new undionly.kpxe to the tftp root.
Now we need a Windows install to install from. Download the Microsoft media creation tool and use it to create an ISO image. I then unzipped the files on my Windows computer using 7-zip, dropping them in the ISO folder.
Now boot the client. It should boot into a Windows graphical command prompt. We now need to connect over Samba to the location of the Windows ISO and run the installer. Be warned, it’s a very slow process, don’t expect anything to happen instantly.
net use \\<ServerIP>\server
\\<ServerIP>\server\html\iso\sources\setup.exe
Since we told Samba earlier that executing anything is fine (who needs security when you’re prototyping), we can execute the installer. We now get a normal-ish Windows GUI installer, loaded over SMB, from the WinPE environment we network booted over HTTP, from the iPXE environment we loaded over PXE, with no physical storage or disks at all on the client. Not bad.
Unfortunately, this win was short lived, as I couldn’t get the installer to partition the iSCSI drive. It found it, and it was blank, but clicking ’new’ would let me go through the process of creating the partition, and then it would hang when actually creating it. Unfortunately, this is the end of my adventure, as I’m sick of dealing with Windows 10. I’ve read (relatively few) guides of this working correctly in Windows 7, but it seems to be a fairly unpopular solution in the modern era.
Conclusions⌗
Is this a good idea in a homelab in 2021? Probably not the best idea. There are far better options for VDI than network booting a Windows installation over iSCSI. Is it an option for the right scenario? Definitely. PXE has its place, and it’s definitely been a fun project to learn about it. The key lesson I’ve learned is that it should be used when you are booting a read-only something, and a whole lot of that something, to justify setting up and configuring a server. If you just want to network boot one media center PC to avoid buying an SSD, this is probably not the solution for you.
Where does network booting have a place?⌗
- For installers, where you network boot into a menu to select which installer you want to run off a network store, and install something on the computer, setting up a PXE server and all of the associated ipxe configuration is fantastic. I found a great example file for that here, although you could easily use an embedded script in ipxe to avoid writing rules in the DHCP configuration. You also have the option of identifying clients by MAC or other unique address to serve the right installer.
- For an application like a computing lab, you could use Linux and mount the root filesystem read-only using NFS, which lets you deal with common ancestry and deduplication at a file level instead of a block level as in iSCSI. You could also mount the home directory read-write using NFS separately, so every computer boots the golden root image and any changes are lost when it reboots (except the user’s home directory). Since all of the computers are physically identical, drivers and such should be the same.
- For a server cluster, it’s not a bad way to distribute the cluster OS and root filesystem either. Again, you’d setup a ‘golden’ image, mount that over iSCSI or NFS, and boot into it. The applications in the cluster must all rely entirely on network storage for this to work, which is a very reasonable expectation of a cluster, and the server would need to configure its networking based on its MAC address. Using DHCP static allocations would be easeiest for this, although if they are all nodes in a cluster they might not need reserved IPs at all and plain DHCP might be fine.
- For home use such as media centers or wall mounted tablets, it might be a decent idea to network boot into a Linux OS which again mounts a read only filesystem designed for this purpose, loading the media over the network. For this to be viable you really need a decent number of nodes, and you really need to be running Linux, which is much less of a headache to network boot. The new Raspberry Pi 4 keeps the first stage boot image in EEPROM instead of on the SD card, meaning you can reprogram it to look for other boot devices, including PXE. The older Pi’s would need an SD card to store the first stage boot image, which could then go out and network boot, leaving the SD card otherwise unused. That said, simply booting a read only filesystem from the SD card and loading the media and configuration off the network would be easier to setup and easy to replace if it breaks.
- For a home lab which uses GPU partitioning, where you lose the ability to use the physical outputs on the GPU (or use GPUs which never had them), you could network boot a Linux image on all of your computers which in turn launches Parsec as the only graphical environment, to connect remotely to the host on the compute server.
- While researching this, I found a number of companies offering software to centrally store the boot disks of Windows corporate workstations, so the company could keep classified data safe overnight without manually removing all of the hard drives from the computers. By removing the drives from the server or keeping it physically secure and disconnected overnight, they could network boot all of the workstations and reduce the chance of physical data theft. There is also some advantage in doing this that you can make a golden corporate image and clone the image for each employees workstation image (or, if you’re really paranoid, wipe them overnight back to the golden image), reducing space on the server since they are all based on a common ancestor, keep central backups of the images, and let any physical workstation boot to anyone’s personal workstation image as required, so no more cloning hard disks when replacing desktops. If your workflow is already heavily based on keeping files on the server and only programs and customizations are stored locally, this could work very well. Linus (of LTT) might be better off network booting all of his editing workstations rather than his living room gaming computer.
What’s a better option for a home lab?⌗
- For a media center or home theater that also does gaming, using VFIO (PCIe passthrough or SR-IOV) to a GPU in a compute server and connecting that via long distance HDMI / DisplayPort / USB3 active optical cables is probably cheaper and less power intense way to get 4K/60 video to the client. You’ll be able to use the compute resources you already have in the rack, and unless you’re getting small form factor desktops which can fit a GPU in bulk, probably save money by only adding the GPU to an existing compute node. If you are gaming, you probably don’t want to deal with the latency of streaming over the network, although apparently Parsec isn’t bad (I haven’t used it myself). The downside is that there is no standard for connecting any of these things over networking fiber, so your cheapest option is usually an active fiber cable instead of using standard networking fiber, which means pulling wires which are essentially already dated when installed (i.e. HDMI 2.0 = no 8K without pulling new wires). You also can’t partition the GPU if you use the physical video outputs, since GPU partitioning was never designed to be used with consumer GPUs in the first place (on either the AMD or nVidia side), and the tricks used to partition Geforce GPUs essentially make the drivers believe it’s an equivalent Quadro or Tesla with the same silicon.
- Give each user a laptop with a locally installed OS and let them connect to hosts in the compute node (which can use GPU partitioning) for workloads that need more compute or GPU performance.
- For a dedicated single user workstation, using dedicated local storage still isn’t an awful choice, it just makes backing up the OS drive more difficult than network booting. The same goes for laptops.
- For both laptops and desktops, you have the option of installing the user’s OS inside a hypervisor and using the hypervisor to manage backups. You may use XCP-NG or Proxmox, pass through the physical GPU to the guest, mount the guest from local storage, and have the hypervisor snapshot and push the backup to the server periodically. You’ll have less space available to the guest due to snapshots, but SSDs are cheap enough now. Re-installing the hypervisor on disk failure and then restoring a backup of the guest disk is probably easier than re-installing the guest OS and all of the programs on it, even if you have good data backups. For this to work, you can’t pass through the entire disk to the guest, since you need to manage snapshots through the hypervisor.
- Installing Windows programs on a network share is possible, with the right permissions in Samba. You can also locate your Steam library on a network drive. Steam games are very easy to reinstall, but tend to be massive, so keeping them backed up locally just saves internet bandwidth if you have a drive failure.
- For macOS, apps are considerably easier to backup since they are usually part of an App Bundle which can be copied freely, so backing up the entire Applications directory isn’t really difficult. There are certainly some apps which don’t respect the App Bundle, but it’s a lot easier than on Windows.
- For Linux, writing an Ansible playbook to install your normal desktop apps on top of a fresh distro install is a pretty decent solution.
I hope to explore some of these options in a future adventure.