Authoritative DNS with BIND on LXC
Contents
Prerequisites
- A Proxmox cluster with at least two nodes — see Getting Started with Proxmox
- VyOS configured with DNS forwarding — see DHCP and DNS Forwarding in VyOS
Overview
Running BIND9 on LXC containers gives the lab a proper authoritative DNS server for internal names, with a secondary for redundancy. Each container is placed on a separate Proxmox node so a node failure does not take down both servers.
VyOS already acts as the DNS resolver for lab hosts. Rather than replacing it, the VyOS forwarder is updated to delegate lab.local queries to BIND while continuing to forward public queries upstream directly.
Creating the LXC Containers
Create two containers — one on each Proxmox node. In the Proxmox web UI, use Create CT with the following settings for each:
| Setting | Value |
|---|---|
| Template | Debian 12 |
| Disk | 4 GB |
| CPU | 1 core |
| Memory | 256 MB |
| Network | lab bridge, static IP |
| Start at boot | yes |
Assign static IPs from outside the DHCP range — for example 10.x.x.2 (ns1, primary node) and 10.x.x.3 (ns2, secondary node). DNS servers must have stable addresses.
Installing BIND9
Run the following on both containers:
| |
Configuring the Primary (ns1)
Options
Edit /etc/bind/named.conf.options:
| |
allow-recursion— limits recursive queries to the lab subnet; prevents the server being used as an open resolverforwarders— upstream resolvers for names outsidelab.localallow-transfer none— zone transfers are blocked globally and enabled per-zone below
Zone Declaration
Edit /etc/bind/named.conf.local:
| |
notify yes— sends a NOTIFY message to all NS records in the zone when it changesalso-notify— explicitly notifies the secondary regardless of whether it appears in the NS records; useful when the secondary’s IP differs from the name in DNS, or to be unambiguous about which server to notify
Zone File
Create the directory and the zone file:
| |
/etc/bind/zones/db.lab.local:
| |
Validate and restart:
| |
Configuring the Secondary (ns2)
Options
Use the same /etc/bind/named.conf.options as the primary but change listen-on to the ns2 IP:
| |
The secondary does not need forwarders — it will receive zone data from the primary and for non-local queries it can forward to upstream directly or rely on the primary. Keep the forwarders block identical for consistency.
Zone Declaration
Edit /etc/bind/named.conf.local on ns2:
| |
Restart:
| |
The secondary will immediately request a zone transfer from the primary. Confirm it succeeded:
| |
Updating VyOS to Use BIND
The VyOS forwarder currently sends all queries to public upstream resolvers. Add a domain override so lab.local queries are sent to BIND instead:
| |
Public queries continue to use the configured name-server entries. Only lab.local is delegated to BIND. No changes to DHCP are needed — lab hosts still receive the VyOS IP as their resolver.
Verifying
From a lab host:
| |
From the Proxmox host or any machine that can reach ns1 directly:
| |
Adding Records
To add a new host, edit /etc/bind/zones/db.lab.local on ns1, increment the serial number, then reload the zone. BIND sends a NOTIFY to ns2 automatically, triggering a zone transfer.
| |
rndc reload lab.local reloads only the affected zone without restarting the daemon, and sends NOTIFY to the addresses in also-notify.
What’s Next
Now that internal hostnames resolve correctly, connections to those services should be secured with HTTPS. Because lab.local is a private domain, Let’s Encrypt cannot issue certificates for it — there is no way to complete a public ACME challenge against an internal name. The solution is to run your own CA and distribute its root certificate to your nodes and browsers.
- Internal CA with OpenSSL — creating a root CA and intermediate CA for issuing TLS certificates for internal services
- VRRP redundancy — adding a second VyOS instance for failover
- BGP peering with Kubernetes — dynamically routing Kubernetes service IPs through VyOS