Published on (
 See this if you're having trouble printing code examples

Building a BSD Netboot Server

by Mikhail Zakharov

When FreeBSD 5.2.1 appeared, the differences in diskless operations from branch 4.x were so great that the handbook needed revising. That correction occurred recently, so the ISO image of the FreeBSD-5.2.1-Release, written on 25.02.2004, included the obsolete version, which applied to FreeBSD-5.1 (at best). Besides, the guide doesn't cover everything; the steps to build the system server-diskless stations are a maze of variants of realizations, technologies, and descriptions of differences between FreeBSD versions.

While trying not to miss the multitude of the possibilities, I've concentrated on only one method to install FreeBSD-5.2.1-Release as a server for diskless clients. For the sake of precision, I have illustrated the process with a concrete example.


As I've confined myself to a single alternative, it's important to lay out some conditions. First, the method here differs from the canonical version in the way it uses rc files in the FreeBSD distribution. For example, diskless stations won't put their file systems in virtual memory; instead, they will use NFS to mount file systems as read-only (/ and /usr) and read-and-write (/etc and /var).

Second, I'll use FreeBSD-5.2.1-Release server for the entire process, along with the following software:

If I don't specify otherwise, install the software from ports.

Related Reading

BSD Hacks
100 Industrial Tip & Tools
By Dru Lavigne

The diskless stations will use the address pool of through The hostname will follow the format "diskless-<the-last-IP-address-octet>", so the hostname "diskless-101" refers to the test diskless workstation (Intel Pentium-90, 16Mb RAM, LAN Intel EtherExpress-100 PXE) with IP address I'll used this station for all examples.

An Overview of the Diskless Booting Process

The main point of the problem is to send a FreeBSD kernel over the network to the computer while it boots. The handbook mentions two ways to do it:

Both PXE and Etherboot use BOOTP or DHCP to receive information about network addresses. The difference between the two is that PXE uses TFTP to fetch a much smaller program for pxeboot, which then itself loads the kernel by NFS or TFTP, while Etherboot uses TFTP or NFS uploads and boots the kernel from the server.

Nowadays, it's easier to buy a new net card supporting the PXE specification than it is to burn PROM into an old adapter, so I'll skip the methods to boot diskless clients using Etherboot or Netboot. I'll only say that it's far easier to set up Etherboot then Netboot for loading the FreeBSD kernel. PXE is the easiest of all, because it doesn't need any additional software.

When loaded on a diskless station, a FreeBSD kernel mounts the root file system on the resource of NFS-server and calls /sbin/init (or a likely substitute, such as init.bak, oinit, or sysinstall), mounts devfs on /dev, executes the /etc/rc script, and forks a getty process for each relevant terminal device listed in /etc/ttys.

The script /etc/rc, which finishes the booting procedure and starts the main system daemons, is worth mentioning. This script is important for this variant of diskless booting for one more reason. While running /etc/rc, the system needs to substitute one of the diskless client's file systems.

For the sake of saving space on the server hard drive, I've decided to share the root file system for all the diskless clients. That's why it's more reasonable to mount it read-only. As an unfortunate result, /etc will be the same for all of the diskless stations and accessible only for reading. In order to provide the flexibility of creating individual /etc directories for every client, I placed a special operation in /etc/rc to mount the stations' /etc in read-write mode.

It's possible to do the same with the default FreeBSD rc scripts, though they'll store /etc in virtual memory. This isn't very good, in my opinion, because the RAM is doubtless the main and often scarce resource for all diskless stations. That's why I used a different approach. For more information, see the scripts /etc/rc.d/initdiskless and /etc/rc.d/diskless.

Installing a diskless station itself requires little effort and only consists of mounting a PXE-supported network card into the computer case. Then, in accordance with the particular station equipment, you have only to choose the right order of the booting--"boot from LAN," in our case, either in the computer's BIOS in the menu of the LAN adapter itself. On the test station, where the BIOS didn't support booting from LAN, I had to do the configuration through the menu of the network adapter. In order to access the menu of the network card, I had to press Ctrl-S while booting the diskless station.

Now it's clear that the set up's principal work takes place on the server. It's time to configure the server.

Server Configuration

To set up FreeBSD as the diskless station server requires configuring NFS, DHCP, and TFTP. The sequence of this configuration doesn't really matter, but let's follow the logic of the boot process and select this order: DHCP, TFTP, NFS.

Install the server as usual, with one small change. To place the client file systems, allocate two separate partitions, one for "reading only" and the other "reading and writing." Mount these file systems into the following directories of the root file system of the server:


In the future, the server will export these file systems with the proper permissions to the diskless clients over NFS. If you can't use these special partitions as the diskless clients file systems, don't make a mistake of putting them on the same physical file system of the server. It would be better to choose subdirectories in /usr, /home or /var. For example, if /usr and /var are different file systems on the server, the client directories can be /usr/diskless_ro and /var/diskless_rw. Such allocation of files corresponds to the NFS specification, which I'll mention again when describing the configuration of the NFS server.

DHCP Server

The first thing required to boot a diskless workstation remotely is to supply it with the information about its IP address, subnet mask, the place of the root file system, and so on. You have a choice of a BOOTP or DHCP server.

Nowadays, BOOTP is rare in comparison with its successor DHCP, so I'll confine myself to the modern alternative. Besides, anyone interested in the configuration of the classic BOOTP server can read about it in the handbook.

For the DHCP server, I use ISC DHCP (isc-dhcp3), which also supports the BOOTP protocol. The basic FreeBSD distribution doesn't include the ISC DHCP, so I installed it from ports. The installation usually resembles:

server# cd /usr/ports/net/isc-dhcp3 && make install clean 

Every diskless client will have NFS shares for writing, so it doesn't make sense to change IP addresses from time to time. In the DHCP server configuration, disable dynamic IP address distribution. Instead, use DHCP in the BOOTP mode (DHCP for BOOTP clients, in the terminology of ISC DHCP).

The main configuration of the DHCP server lives in the file /usr/local/etc/dhcpd.conf. Edit this file in order to make the simple configuration. As there is only one server and one subnet, almost all of this information goes into the global parameters section:

# Server is authoritative for the subnets.

# Disable dynamic DNS updates.
ddns-update-style none;            

# Send the client the "hostname", specified in the option
# "host" in the host declaration.
use-host-decl-names on;            

# Set the default gateway.
option routers;

# The server from which to upload the initial boot-file.

# The file to load from the "next-server".
filename "pxeboot";

# The shared root file system of diskless workstations
option root-path "";     

# Network definition.  There are no options because of 
# dynamic distribution of IP-addresses is disabled.
# But DHCP refuses to load without this line, so we
# can't remove it.
subnet netmask { }     

# Diskless station definitions: we must specify
# hostname, IP and MAC addresses for each client

host diskless-10 {                                      
# Hostname which will be sent to the client because of 
# "use-host-decl-names on"

		# Ethernet-address of the workstation LAN adapter
        hardware ethernet 00:0D:9D:8B:BB:48;            

		# Workstation IP-address.


host diskless-101 {
        hardware ethernet 00:0C:29:74:2B:35;


host diskless-254 {
        hardware ethernet 00:02:B3:8A:D6:BF;

Now start the DHCP server:

server# /usr/local/sbin/dhcpd 

If it loads successfully, you'll see a message similar to this:

Internet Software Consortium DHCP Server V3.0.1rc12
Copyright 1995-2003 Internet Software Consortium.
All rights reserved.
For info, please visit
Wrote 0 deleted host decls to leases file.
Wrote 0 new dynamic host decls to leases file.
Wrote 0 leases to leases file.
Listening on BPF/fxp0/00:0c:29:f8:78:fc/
Sending on BPF/fxp0/00:0c:29:f8:78:fc/
Sending on Socket/fallback/fallback-net

Run this to start DHCP at boot time:

server# cd /usr/local/etc/rc.d
server# mv
server# chmod 755

TFTP Server

There are three pieces of information that the clients need to know to download the appropriate booting code from the TFTP server:

A little later, they also need the root-path option to load the kernel and mount the root file system. For now, the PXE LAN adapter of the station automatically tries to use two of the other parameters to continue booting. The pxeboot(8) file is a modified version of the loader(8), which runs on the third stage of the FreeBSD booting process (see the handbook for more details about the boot procedure). As the diskless client expects to fetch this program via TFTP, it must be exist on the server at

The FreeBSD distribution includes a TFPT server, so you do not need to install additional software. The daemon is tftpd and usually starts from inetd. To start TFTP server, create a special directory of /tftpboot and copy pxeboot there:

server# mkdir /tftpboot
server# cp /boot/pxeboot /tftpboot

Add the following line into /etc/inetd.conf:

tftp dgram udp wait root /usr/libexec/tftpd tftpd -l -s /tftpboot

The -l switch turns on the logging of TFTP operations. The -s switch specifies the root directory for tftpd after it calls chroot(). For more details about tftpd and chroot(), see man tftpd(8) and chroot(2).

The server is ready to run after you restart inetd:

server# killall -HUP inetd 

If everything is okay, the command

server# sockstat -4l | grep 69 

will return results similar to this:

root inetd 556 5 udp4 *:69 *:* 

NFS Server

After the client successfully downloads the pxeboot file, according to the root-path option, it'll try to connect over NFS to the /diskless_ro directory of the server to find the root filesystem with an appropriate kernel there.

You can also configure pxeboot to upload a kernel with TFTP. This will allow you to boot different diskless stations with different kernels. In that case, you need to recompile pxeboot with the option LOADER_TFTP_SUPPORT=YES in /etc/make.conf. See also the handbook and /usr/share/examples/etc/make.conf.

I confined the example network to using one kernel for all the workstations. As a consequence, I set up the NFS server to export the proper directories. As the name implies, the directory /diskless_ro should export as read-only. The diskless_rw directory contains subdirectories specific to each client for its writing. Each subdirectory must itself contain the special etc and var directories. For example, the test diskless workstation will have its own directory, diskless_rw/, with two subdirectories named /diskless_rw/ and /diskless_rw/

The directory /diskless_ro should be empty, while /diskless_rw contains something like:


Besides these two exports, the diskless station will use /usr from the server in read-only mode.

In order to let the diskless station use all of these directories, you must configure the NFS server accordingly. Add the following lines to the file /etc/exports on the server:

# file systems accessible only for reading:
/usr -ro -maproot=0 -network -mask
/diskless_ro -ro -maproot=0 -network 

# file systems accessible for writing. All the resources
# given to every diskless station are specified by one line:
# Diskless-10
/diskless_rw/ /diskless_rw/ \

# ...

# Diskless-101
/diskless_rw/ /diskless_rw/ \

# ...

# Diskless-254
/diskless_rw/ /diskless_rw/ \

Then change /etc/rc.conf to start the NFS server while the system boots:


You may also need to change the nfs_server_flags variable:

nfs_server_flags="-u -t -n 48 -h" 

The -n switch is very important here. It specifies the number of nfsd daemons that regulate the NFS, which influences the number of the NFS clients that can connect simultaneously. Tune this parameter according to the number of clients. The -u and -t switches turn on UDP and TCP. -h binds the daemon to a network interface.

Now, start the NFS server by hand (so as not to have to reboot the server):

server# rpcbind
server# nfsd -u -t -n 48 -h
server# mountd -r

After the NFS server starts correctly, check the exported file systems:

server# showmount -e
Exports list on localhost:

Notes on Mounting

It is not a good idea to place diskless_rw and diskless_ro within the same physical file system because NFS doesn't export the directory but the whole file system. In /etc/exports, every line represents of the export of one server file system to one or several clients. For each exported file system, you can specify the same client only once.

For example, if diskless_rw and diskless_ro occupy different file systems, then this /etc/exports will be correct:

/diskless_ro -ro

A mistaken /etc/exports might be:

/usr/diskless_ro -ro

If diskless_rw and diskless_ro are directories of the same file system /usr, an error will occur while exporting them to the same clients, which will prevent them from mounting diskless_ro. The rules demand that you specify both resources, /usr/diskless_rw and /usr/diskless_ro on one line, so you have to decide whether to make them both accessible for reading only or for both reading and writing.

Nevertheless, you can deceive the mountd daemon by using IP address ranges instead of hostnames. For example, here's another mistaken version of /etc/exports that will execute successfully:

/usr/diskless_rw -network -mask
/usr/diskless_ro -ro -network -mask

In this case, the server will successfully export /usr/diskless_rw and /usr/diskless_ro. As this configuration handles the whole file system and not only the directories, the subnet will be able to mount both /usr/diskless_rw and /usr/diskless_ro in read-write mode, so there are security risks.

Mikhail Zakharov is presently the senior UNIX Administrator in a Moscow banks where he administers a wide spectrum of servers running various UNIX-like operating systems.

Return to the BSD DevCenter

Copyright © 2009 O'Reilly Media, Inc.