Table of Contents
OpenBSD is one of the most security-focused operating systems available and ships with PF (Packet Filter) — a powerful, readable firewall syntax. Running it as a VM on your hypervisor lets you place it "in front" of all your other VMs without needing dedicated hardware, and lets you test changes safely before applying them to production.
Final Architecture
The hypervisor carries all VLANs on a LAG (Link Aggregation Group) trunk — VLANs are configured on a bonded bridge and passed through to the switch ports the hypervisor connects to. The OpenBSD VM gets multiple virtual NICs, one per VLAN, each corresponding to a separate subnet. The physical switch tags traffic to the correct VLAN per port.
Switch Port Configuration
| Port / Interface | Mode | Purpose |
|---|---|---|
| Hypervisor uplink | Trunk (all VLANs) | Carries all VLAN traffic to/from hypervisor |
| WAN port | VLAN (ISP/WAN) | Modem → switch → hypervisor WAN NIC |
| WiFi AP port | Trunk (home VLAN) | Passes home VLAN to AP for client WiFi |
| Server ports | Access (apps or data) | Each server port assigned to its VLAN untagged |
On a FreeBSD host using the vm-bhyve framework, create a VM config file for OpenBSD. The config below is the exact working config used in production.
For a multi-VLAN router add additional network interfaces — one per VLAN. Each maps to a separate vm-bhyve virtual switch:
-
1
Create the VM and Disk
vm create -t openbsd -s 20G openbsd-router vm install openbsd-router OpenBSD-7.0-amd64.iso -
2
Connect to the Console
vm console openbsd-routerOpenBSD's serial console is activated by the
-h com0flag in the grub config. You'll see the OpenBSD installer via the console.
-
1
Walk Through the Installer
At the
boot>prompt, press Enter. The installer asks a series of questions:- Keyboard layout: default (us)
- Hostname: e.g. fw01
- Network interface: configure vio0 with a temporary IP to reach a mirror
- Root password: set a strong one
- Enable sshd: yes
- Disk layout: use whole disk / auto layout for simplicity
- Sets to install: all defaults are fine; you can deselect
game73.tgz
-
2
First Boot — Disable Console Beep
echo "set tty com0" >> /etc/boot.confThis ensures serial console persists across reboots when running headless in bhyve.
-
3
Update Packages
syspatch # apply OS security patches pkg_add vim wget # install any tools you need
OpenBSD configures each interface via a /etc/hostname.vioN file. The WAN interface gets its IP from DHCP (from the modem/ISP). Each LAN interface gets a static IP — the gateway for that VLAN's subnet. Additional config files handle the rest: pf.conf contains the firewall rules, dhcpd.conf defines DHCP ranges per subnet, sysctl.conf enables IP forwarding, and rc.conf.local controls which daemons start at boot.
Bring all interfaces up:
OpenBSD does not forward packets between interfaces by default. Enable it permanently:
Apply immediately:
OpenBSD's built-in dhcpd serves DHCP leases. Configure a subnet block for each LAN interface.
Enable and start dhcpd, specifying which interfaces to listen on:
The following is the actual pf.conf used in production.
Reload PF rules at any time without interrupting connections:
pass in from any) allows SSH from the internet. For production, restrict this: pass in on $wan inet proto tcp from <trusted_ip> to ($wan) port 22set block-policy return instead of the default drop. With return, PF sends an immediate TCP reset or ICMP unreachable to blocked connections rather than silently dropping them — resulting in faster failure feedback for clients instead of waiting for a connection timeout.relayd is OpenBSD's built-in relay daemon — it handles reverse proxying, load balancing, and TLS termination. Used here to forward inbound HTTPS to internal services. Note that relayd depends on PF and must run on the same OpenBSD instance; it cannot be used independently of the PF firewall.
Enable and start:
-
1
Test as Downstream Router First
Assign the OpenBSD VM a spare VLAN and secondary subnet. Plug a test device into that VLAN and verify it gets a DHCP lease, can ping the OpenBSD gateway, and has internet. Only then proceed to cut over production.
-
2
Move the Modem Connection
Point the ISP modem/ONT to the switch port or VLAN that feeds the OpenBSD VM's WAN interface. Verify
vio0gets a public IP via DHCP from the ISP. -
3
Verify DHCP Leases on All VLANs
cat /var/db/dhcpd.leases # confirm leases being issued rcctl check dhcpd -
4
Monitor PF in Real-Time
pfctl -ss | grep ESTABLISHED # active connections tcpdump -n -e -i vio0 # watch WAN traffic -
5
Shut Down Your Existing Router
Once all VLANs are routing correctly and internet is stable, shut down your previous router. Keep it powered off but available (or as a snapshot/backup) for quick rollback during the first few days.