OpenBSD Handbook

    Theme
    • Part I. Install & Configure
      • Introduction to OpenBSD
      • Installing OpenBSD
      • The X Window System
      • Networking on OpenBSD
      • System Configuration
      • OpenBSD Basics
      • Managing Software: Packages and Ports
    • Part II. Daily Operations
      • OpenBSD Desktop Environments
      • Multimedia
      • Printing
      • Linux Compatibility
      • Windows Compatibility
      • Games
    • Part III. System Administration
      • Virtualization
      • Storage and File Systems
      • Updating and Upgrading
      • Localization
      • The OpenBSD Boot Process
      • Security
    • Part IV. Networking & Daemons
      • Services
        • Database
          • MariaDB
          • PostgreSQL
          • Redis
          • memcached
        • Directory
          • YP (NIS)
          • LDAP
        • File
          • NFS
          • Samba
        • FTP Services
          • ftpd
          • ProFTPD
          • vsftpd
          • Pure-FTPd
          • TFTP
        • Mail
          • OpenSMTPD Mail Server
          • Dovecot IMAP and POP3 Server
          • Postfix
          • Exim
          • Rspamd
        • Name
          • Unbound DNS Resolver
          • NSD Authoritative DNS Server
          • BIND DNS Server
        • Networking
          • OpenBGPD
          • rad
          • DHCP on OpenBSD
          • slaacd
        • Web
          • Apache HTTP Server
          • nginx Web Server
          • OpenBSD httpd Web Server
          • relayd Reverse Proxy
        • Logging
          • syslogd
        • Monitoring
          • SNMP
        • Remote Access
          • Audit OpenSSH
          • OpenSSH Server
        • File Synchronization
          • rsync
        • Messaging
          • RabbitMQ Message Broker
        • Time
          • NTP Time Synchronization
        • Load Balancing
          • Build Redundant Load-Balanced Gateways
          • Load Balance Services with NAT or DSR
          • Build an HA HTTPS Load Balancer
      • PF
        • pfctl cheat sheet
        • PF Anchors
        • PF Filter Rules
        • PF Forwarding
        • PF Lists and Macros
        • PF Load Balancing
        • PF Logging
        • PF NAT
        • PF Options
        • PF Policies
        • PF Shortcuts
        • PF Tables
      • Advanced Networking
        • High Availability and State Replication
        • Multi-WAN and Policy-Based Routing
        • VPN and Cryptographic Tunneling
        • Classic and Lightweight Tunnels
        • IPv6 at Scale
        • QoS and Traffic Shaping
        • MPLS and Label Distribution
        • Network Services at Scale
        • Virtualization and Host Networking
        • Large-Scale L2 and L3 Design
        • Telemetry, Logging, and Flow Export
        • Hardening and Operational Safety
        • Reference Architectures
        • Troubleshooting Playbooks
      • Serial Communication
    • Part V. Miscellaneous
      • Virtualization Cheat Sheet
      • OpenBSD Cheatsheet
      • How-to Guides
        • Installation and First Boot
          • Complete the First 30 Minutes After Installation
          • Check Hardware Compatibility Before Installing
          • Bootstrap Wi-Fi Firmware Without Ethernet
          • Run OpenBSD as a Virtual Machine Guest
          • Install OpenBSD on a VPS or Cloud Server
          • Install OpenBSD with Full-Disk Encryption
          • Dual-Boot OpenBSD on a UEFI System
          • Automate OpenBSD Installation with Autoinstall and PXE
        • Desktops and Laptops
          • Set Up OpenBSD as a Laptop or Workstation
          • Configure Laptop Power Management
          • Troubleshoot Web Browsers on OpenBSD
          • Use Plan 9 Tools on OpenBSD
          • Build a Minimal Desktop with suckless Tools
          • Install Z shell (zsh)
        • Networking and Remote Access
          • Create an Administrator Account with doas and SSH Keys
          • Configure Secure Remote Access with OpenSSH
          • Use FIDO Security Keys with OpenSSH
          • Configure Wi-Fi Roaming and Wired Failover
          • Troubleshoot PF, NAT, and Routing
          • Route Multiple VLANs with PF
          • Configure a Road-Warrior WireGuard VPN
          • Route IPv6 Networks without NAT
          • Test OpenBGPD with Two Providers
          • Unlock an Internal Service with an SSH Key
          • Build a Simple Router and Firewall
        • Servers and Services
          • Set Up WordPress on OpenBSD
          • Build a Mail Server with OpenSMTPD, Dovecot, and Rspamd
        • Security, Maintenance and Recovery
          • Establish a Conservative Security Baseline
          • Recover Access and Reset the Root Password
          • Collect OpenBSD Diagnostic Evidence
          • Troubleshoot Package Installation and Update Failures
          • Schedule Recurring Maintenance
          • Monitor an OpenBSD System
          • Back Up and Restore an OpenBSD System
          • Configure softraid RAID1
          • Recover an OpenBSD System That Does Not Boot
        • Building, Automation and Virtual Machines
          • Manage OpenBSD with Ansible
          • Build Third-Party Software from Ports
          • Build OpenBSD from Source
          • Plan a Docker Workload Migration to OpenBSD
      • OpenBSD for Linux Users
      • OpenBSD for FreeBSD Users
      • OpenBSD for macOS Users
    • OpenBSD FAQ
    • OpenBSD Architectures
      • OpenBSD Package Search
        • Synopsis
        • Design Considerations
        • Configuration
          • 1) Authoritative DNS with nsd(8)
          • 2) Validating recursive DNS with unbound(8)
          • 3) DHCP for IPv4 with dhcpd(8)
          • 4) Time service with ntpd(8)
          • 5) PF allowances for DNS, DHCP, and NTP
        • Verification
        • Troubleshooting
        • See Also

        Network Services at Scale

        Synopsis #

        Configure DNS, DHCP, and time services on OpenBSD: authoritative DNS with nsd(8) configured via nsd.conf(5) , validating recursive DNS with unbound(8) configured via unbound.conf(5) , IPv4 address allocation with dhcpd(8) configured via dhcpd.conf(5) , and time services with ntpd(8) configured via ntpd.conf(5) . Service lifecycle management uses rcctl(8) . Packet filtering allowances live in pf.conf(5) and are applied with pfctl(8) . Use these patterns for branch offices, campus networks, and PoPs where you operate DNS, DHCP, and NTP with redundancy.

        Design Considerations #

        • Separation of roles. Bind authoritative and recursive DNS to different IP addresses. A common pattern is NSD listening on a public/WAN address (or a dedicated service VIP) and Unbound listening on LAN addresses.
        • Redundancy. Use at least two servers per critical service. Combine with CARP and pfsync from the HA chapter for failover of service VIPs.
        • Security posture. Permit UDP/TCP 53 from the Internet only to authoritative servers. Restrict recursion to inside prefixes. Enable DNSSEC validation in Unbound.
        • DNS roles. NSD does not support dynamic updates; keep zone management explicit and version-controlled. Unbound is not authoritative; use stub zones to prefer local authoritative answers when both run on the same host.
        • IPv6. Provide AAAA records in authoritative zones and ensure recursive service answers both A and AAAA. Address assignment for IPv6 prefers RA (see the IPv6 chapter); DHCPv6 is out of scope here.
        • Time service. DNSSEC validation requires correct time. Ensure ntpd(8) is running on resolvers and that clients have a local NTP source.

        Configuration #

        Assumptions for examples:

        • Interfaces: em0 (WAN), em1 (LAN 10.10.10.0/24).
        • Service IPs: 203.0.113.53 (authoritative DNS on WAN), 10.10.10.1 (recursive DNS and NTP on LAN).
        • Authoritative zone: example.com. served by NSD.
        • Local recursion for LAN clients via Unbound.

        1) Authoritative DNS with nsd(8) #

        Bind NSD to the public address and serve example.com. from a local zone file. Control socket is enabled for safe reloads.

        ## /var/nsd/etc/nsd.conf — NSD authoritative service on WAN address
        
        server:
            ip-address: 203.0.113.53
            hide-version: yes
            do-ip4: yes
            do-ip6: no
            server-count: 1
            zonesdir: "/var/nsd/zones"
        
        remote-control:
            control-enable: yes
            control-interface: /var/run/nsd.sock
        
        zone:
            name: "example.com"
            zonefile: "example.com.zone"
        

        Create the zone file (minimal example; expand as required). The SOA and NS records must be correct for production.

        ; /var/nsd/zones/example.com.zone — minimal zone
        
        $ORIGIN example.com.
        $TTL 300
        @   IN SOA ns1.example.com. hostmaster.example.com. (
                2025010101 ; serial (YYYYMMDDNN)
                3600       ; refresh
                600        ; retry
                1209600    ; expire
                300        ; minimum
        )
            IN  NS  ns1.example.com.
        ns1 IN  A   203.0.113.53
        www IN  A   203.0.113.80
        api IN  A   203.0.113.81
        

        First-time control key setup and service start:

        doas nsd-checkconf /var/nsd/etc/nsd.conf
        doas nsd-checkzone example.com /var/nsd/zones/example.com.zone
          # Stop and correct any errors before starting
        doas rcctl enable nsd
          # Start on boot
        doas rcctl start nsd
          # Launch now
        doas nsd-control reload
          # Validate and load the zone
        

        2) Validating recursive DNS with unbound(8) #

        Unbound will listen on LAN, serve only inside clients, and validate DNSSEC. When NSD and Unbound share a host, forward a local zone to NSD via a stub to avoid conflicts on port 53 by running NSD on WAN and Unbound on LAN.

        ## /var/unbound/etc/unbound.conf — LAN recursion with validation
        
        server:
            interface: 127.0.0.1
            interface: 10.10.10.1
            access-control: 10.10.10.0/24 allow
            access-control: 127.0.0.0/8 allow
        
            # Hardening and privacy
            hide-identity: yes
            hide-version: yes
            qname-minimisation: yes
            harden-referral-path: yes
            harden-glue: yes
            prefetch: yes
        
            # DNSSEC
            auto-trust-anchor-file: "/var/unbound/db/root.key"
        
            # Built-in root hints are sufficient; no external hints file is required.
            do-not-query-localhost: no
        
        remote-control:
            control-enable: yes
            control-interface: /var/run/unbound.sock
        
        # Prefer local authoritative answers (optional internal zone served by NSD)
        stub-zone:
            name: "corp.example."
            stub-addr: 127.0.0.1@5353
        

        If you need Unbound to prefer a local zone while NSD keeps port 53 on WAN, bind NSD additionally to 127.0.0.1 port 5353 and serve corp.example. there (example below). Otherwise omit the stub-zone.

        ## Excerpt — additional NSD listener for an internal zone (loopback)
        
        server:
            # ... existing server block ...
            ip-address: 127.0.0.1@5353
        
        zone:
            name: "corp.example"
            zonefile: "corp.example.zone"
        

        Initialize Unbound control and start the resolver:

        doas unbound-anchor -a /var/unbound/db/root.key
        doas unbound-checkconf /var/unbound/etc/unbound.conf
          # Stop and correct any errors before starting
        doas rcctl enable unbound
        doas rcctl start unbound
        doas unbound-control status
          # Confirm validator is ready and loaded
        

        3) DHCP for IPv4 with dhcpd(8) #

        Serve 10.10.10.0/24, advertise the local resolver and default gateway, and provide a static mapping example.

        ## /etc/dhcpd.conf — IPv4 DHCP on LAN
        
        option domain-name "corp.example";
        option domain-name-servers 10.10.10.1;
        option routers 10.10.10.1;
        option subnet-mask 255.255.255.0;
        default-lease-time 3600;
        max-lease-time 7200;
        authoritative;
        
        subnet 10.10.10.0 netmask 255.255.255.0 {
            range 10.10.10.100 10.10.10.200;
        
            # Static reservation example
            host printer-01 {
                hardware ethernet 00:11:22:33:44:55;
                fixed-address 10.10.10.50;
            }
        }
        

        Enable the daemon and bind it to the LAN interface:

        doas rcctl enable dhcpd
        doas rcctl set dhcpd flags em1
          # Specify served interfaces (restrict service to LAN)
        doas rcctl start dhcpd
        doas tail -n 20 /var/db/dhcpd.leases
          # Inspect active leases
        

        For IPv6 address assignment, prefer Router Advertisements with rad(8) as shown in the IPv6 chapter.

        4) Time service with ntpd(8) #

        Provide an internal NTP source on the LAN and maintain correct time on the server itself.

        ## /etc/ntpd.conf — serve LAN and sync from upstream pool
        
        listen on 10.10.10.1
        servers pool.ntp.org
        
        doas rcctl enable ntpd
        doas rcctl start ntpd
        doas ntpctl -s status
          # Report synchronization state
        

        5) PF allowances for DNS, DHCP, and NTP #

        Permit external queries to NSD on WAN, recursive service on LAN, DHCP on LAN, and NTP for clients.

        ## /etc/pf.conf — service allowances (merge into your policy)
        
        set skip on lo
        
        wan = "em0"
        lan = "em1"
        
        # Base policy
        block all
        
        # Authoritative DNS on WAN (NSD)
        pass in on $wan proto { udp tcp } to 203.0.113.53 port 53 keep state
        
        # Recursive DNS on LAN (Unbound)
        pass in on $lan proto { udp tcp } to 10.10.10.1 port 53 keep state
        
        # DHCP on LAN (server replies from 67 to client port 68)
        pass in on $lan proto udp from port 68 to port 67 keep state
        pass out on $lan proto udp from port 67 to port 68 keep state
        
        # NTP on LAN
        pass in on $lan proto udp to 10.10.10.1 port 123 keep state
        
        # Resolver and NSD outbound as needed
        pass out on $wan proto { udp tcp } to any port { 53, 123 } keep state
        

        Reload after editing:

        doas pfctl -f /etc/pf.conf
          # Load rules atomically
        

        Verification #

        • Authoritative zone served by NSD (from a remote host or the firewall itself), using dig(1):
        dig @203.0.113.53 example.com SOA
          # Expect the SOA you configured
        dig @203.0.113.53 www.example.com A
          # Expect 203.0.113.80
        
        • Recursive resolution with DNSSEC validation (from a LAN client):
        dig @10.10.10.1 cloudflare.com A
          # General recursion works
        dig @10.10.10.1 dnssec-failed.org A +dnssec
          # Should return a validation failure (bogus), consistent with validation; compare a signed valid domain and repeat with +cdflag to distinguish validation from other failures
        
        • Unbound and NSD control:
        doas unbound-control list_stubs
          # Confirm any stub-zones are loaded
        doas nsd-control stats
          # Basic qps/counters for authoritative service
        
        • DHCP leases and client config:
        doas grep dhcp /var/log/daemon | tail -n 20
          # Server activity
        doas cat /var/db/dhcpd.leases | tail -n 10
          # Lease database updates
        
        • NTP status:
        ntpctl -s peers
          # Upstream peers and offsets
        ntpctl -s status
          # Overall sync status
        

        Troubleshooting #

        • Port conflict on 53. NSD and Unbound cannot both bind the same IP:port. Bind NSD to the WAN address and Unbound to LAN addresses, or move one to 127.0.0.1@5353 and use Unbound stub-zone for the internal domain.
        • Open resolver exposed. If outside hosts can recurse, restrict Unbound with access-control to inside prefixes only and confirm PF denies WAN access to the recursive listener.
        • DNSSEC failures. Validation requires correct system time and a current trust anchor. Ensure ntpd(8) is synchronized and that Unbound has a valid auto-trust-anchor-file.
        • Authoritative zone not loading. Check nsd-control reload output and syslog for syntax errors. Validate SOA/NS correctness and serial increments.
        • DHCP not serving. Confirm rcctl set dhcpd flags <lan-ifaces> and that PF allows UDP 67/68 on the LAN. Inspect /var/db/dhcpd.leases and logs for pool exhaustion or MAC mismatches.
        • Clients ignore NTP. Ensure LAN clients point to 10.10.10.1 for NTP and that PF allows UDP 123 from LAN to the server.

        See Also #

        • Networking
        • OpenBGPD
        • Related: High Availability and State Replication
        • Related: Telemetry, Logging, and Flow Export
        • Related: IPv6 at Scale
        Report a bug
        • Synopsis
        • Design Considerations
        • Configuration
          • 1) Authoritative DNS with nsd(8)
          • 2) Validating recursive DNS with unbound(8)
          • 3) DHCP for IPv4 with dhcpd(8)
          • 4) Time service with ntpd(8)
          • 5) PF allowances for DNS, DHCP, and NTP
        • Verification
        • Troubleshooting
        • See Also