Eric Radman : a Journal

OpenBSD Auto-Install

OpenBSD has a very good story for automated installations, which includes features such as:

Demo Install on PC Engines APU2

webm | mp4

Mirroring Sets

You certainly want a local copy of the OpenBSD installation before you begin. The OpenBSD project kindly makes rsync mirrors available to make this task easy

cd /var/www/htdocs/pub/OpenBSD
mkdir -p 7.0/amd64 syspatch/7.0/amd64

cd /var/www/htdocs/pub/OpenBSD
openrsync -rv rsync:// 7.0/
openrsync -rv rsync:// syspatch/7.0/


The first task of booting via PXE is to hand out an address and name the file to fetch over TFTP

# /etc/dhcpd.conf
option  domain-name "";
option  domain-name-servers;

subnet netmask {
    option routers;

    host db1 {
        hardware ethernet fe:e1:bb:d1:cd:29;
        filename "auto_install";
        option host-name "db1";

The filename auto_install is not arbitrary, this causes the install script in bsd.rd to start the automated install by pulling configuration over HTTP. The complete layout for /tftpboot dight look like this:

drwxr-xr-x   2 root  eradman      512 May 16 15:54 .
drwxr-xr-x  16 root  wheel       1024 May 13 01:26 ..
lrwxr-xr-x   1 root  eradman       13 May 16 15:52 auto_install -> pxeboot.amd64
lrwxr-xr-x   1 root  eradman       12 May 16 15:54 bsd -> bsd.rd.amd64
-rw-r--r--   1 root  eradman  776.512 May 13 10:07 bsd.rd.amd64
-rw-r--r--   1 root  eradman    82300 May 13 10:07 pxeboot.amd64

If you are installing a headless system, you need to provide the console parameters to OpenBSD's pxeboot by creating /tftpboot/etc/boot.conf

stty com0 115200
set tty com0

Per-Host Install Options

The next-server entry specified by the DHCP server points to the path where answers file can be found:

default - - [16/May/2016:15:55:54 -0400]
  "GET /00:1e:c9:4c:69:59-install.conf?path=7.0/amd64 HTTP/1.0" 200 314

The answers file contains strings which match the questions from the installer

# /var/www/htdocs/00:1e:c9:4c:69:59-install.conf
System hostname = db1
Password for root = 123456
Network interfaces = run0
IPv4 address for run0 = dhcp
Setup a user = eradman
Password for user eradman = zzzzzz
Public ssh key for user = ssh-ed25519 XYZ123...
Which disk is the root disk = sd0
What timezone are you in = US/Eastern
Unable to connect using https. Use http instead = yes
Location of sets = http
Server =
Set name(s) = -all bsd* base* etc* man* site* comp*

If you don't specify a line then a default will be used. If more options are


Custom partition layouts are documented under the disklabel(8) man page. This is all we need to add to the autoinstall script:

URL to autopartitioning template for disklabel =

Sane mount options are selected automatically. The minimum in a range is selected for each file system, and remaining space is split between the partitions based on the percentage:

/           250M
swap        80M-256M  10%
/tmp        120M-4G    8%
/var        80M-4G    13%
/usr        900M-2G    5%
/usr/local  2G-10G    10%
/pg_data    1G-*      45%


To make this mechanism portable I run these services from my laptop. First I enable the hotplug daemon

# rcctl enable hotplugd

Next create /etc/hotplug/attach to add any USB-to-Ethernet adapter to a local bridge that is serving DHCP requests



case $DEVCLASS in
    ifconfig $DEVNAME up
    ifconfig bridge0 add $DEVNAME

Where DEVCLASS 3 is a network interface. Similarly, /etc/hotplug/detach disables these services using the opposite actions.

Custom Sets

OpenBSD allows for custom software to be installed by adding a site-specific tgz file. If index.txt includs the new file it will appear in the menu; we only need to select the new package in *install.conf

Set name(s) = site67.tgz

To make this easy I am allowing for this one package to be installed without being signed.

Checksum test for site67.tgz failed. Continue anyway = yes
Unverified sets: site67.tgz. Continue without verification = yes


One of the most interesting files that can be installed with siteNN.tgz is /etc/rc.firsttime. This is executed the first time a system boots up in multi-user mode, and is a very convenient way to make sure some bits of essential post-install configuration occur. This example fetches and installs ports on first boot

ftp -o - | tar -zxf - -C /usr

The autoinstaller allows you to skip checkums and signatures for the package, but it has to be listed in index.txt

(cd site67; tar -czvf ../amd64/site67.tgz .)
(cd amd64; ls -l > index.txt)

Another useful item to put in siteXX.tgz is the public signify(1) key. More on that later...

Autoinstalling OpenBSD on VMM

In addition to PXE-booting a bare-metal system we can install local VMs in almost the exact same manner. The first step is to define the VMs.

switch "local" {
    add vether0

vm "db1" {
    memory 512M
    boot "/bsd.db1"
    disk "/vm/db1.img"
    interface {
        switch "local"
        lladdr fe:e1:bb:d1:cd:29

vm "db2" {
    memory 512M
    boot "/bsd.db2"
    disk "/vm/db2.img"
    interface {
        switch "local"
        lladdr fe:e1:bb:d1:cd:30

VMM's boot option can be a BIOS or a kernel image. The trick to auto-installing a VM is to temporarily swap in bsd.rd in order to get to the installer. Now you can type vmctl start db1 -c and press A. We can have tmux automate this for us

#!/bin/sh -ex
# - Reset all VMs
# - Start each VM and select Autoinstall
# - Set the boot kernel to bsd.sp

for vm in $*; do
    ln -f /bsd.rd /bsd.$vm

rcctl restart vmd

for vm in $*; do
    tmux new-session -s autoinstall -d
    tmux send-keys -t autoinstall:0 "doas vmctl start $vm -c" C-m
    sleep 10
    tmux send-keys -t autoinstall:0 "A" C-m
    sleep 2
    tmux kill-session -t autoinstall
    ln -f /bsd.sp /bsd.$vm
doas vmctl status

Signing Custom Packages

Once we can spin up fresh VMs or bare-metal installations we can build custom packages without having to run pkg_add with -Dunsigned. To do this we need to generate a signing key

signify -G -n -s /etc/signify/custombuild-pkg.sec -p /etc/signify/

After building our custom packages sign each of them

pkg_sign -C -v \
  -s signify2 -s /etc/signify/custombuild-pkg.sec \
  -S /usr/ports/packages/amd64/all \
  -o /var/www/htdocs/pub/OpenBSD/7.0/packages/amd64

Adding -C to pkg_sign will take care of updating the checksum file (SHA256) in the target directory.