<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Linux on Welcome to my photography blog</title><link>https://pvdplaat.nl/en/tags/linux/</link><description>Recent content in Linux on Welcome to my photography blog</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Fri, 08 May 2026 09:00:43 +0200</lastBuildDate><atom:link href="https://pvdplaat.nl/en/tags/linux/index.xml" rel="self" type="application/rss+xml"/><item><title>Building a High-Availability DNS Cluster on Debian</title><link>https://pvdplaat.nl/en/blog/high-availability-dns-cluster/</link><pubDate>Fri, 08 May 2026 09:00:43 +0200</pubDate><guid>https://pvdplaat.nl/en/blog/high-availability-dns-cluster/</guid><description>&lt;img src="https://pvdplaat.nl/images/2026/cluster-computing.png" alt="Featured image of post Building a High-Availability DNS Cluster on Debian" /&gt;&lt;h1 id="high-availability-dns-cluster-technitium--keepalived-ipvs-on-debain-lxc-container"&gt;High-Availability DNS Cluster: Technitium + Keepalived (IPVS) on Debain LXC Container
&lt;/h1&gt;&lt;p&gt;Domain Name System (DNS) is the backbone of any network infrastructure. When your DNS servers go down, your entire network effectively stops functioning. While many network administrators simply deploy two separate DNS servers and list both in their DHCP scope, this approach does not provide true load balancing or instantaneous failover.&lt;/p&gt;
&lt;p&gt;In this guide, we will build a production-grade, highly available DNS cluster using &lt;strong&gt;two Technitium DNS servers&lt;/strong&gt; running inside Proxmox Linux Containers (LXC). We will leverage &lt;strong&gt;Keepalived&lt;/strong&gt; and &lt;strong&gt;IPVS (IP Virtual Server)&lt;/strong&gt; to achieve both seamless load balancing and intelligent failover over a single &lt;strong&gt;Virtual IP (VIP)&lt;/strong&gt; address.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="why-standard-secondary-dns-isnt-enough"&gt;Why Standard Secondary DNS Isn&amp;rsquo;t Enough
&lt;/h2&gt;&lt;p&gt;Most home labs and enterprise environments deploy two DNS servers (Primary and Secondary). They hand out both IP addresses via DHCP. However, the client-side implementation of this is flawed:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Unpredictable Routing:&lt;/strong&gt; Some operating systems (like Windows) stick to the primary server until it completely times out, causing severe delays during an outage. Other systems (like macOS/Linux) might query both semi-randomly, making traffic distribution unpredictable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No Real Load Balancing:&lt;/strong&gt; Traffic is rarely distributed evenly between the two servers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Slow Failover:&lt;/strong&gt; If the primary DNS server freezes but keeps its network link active, clients will waste precious seconds waiting for timeouts before trying the secondary server.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="the-solution-keepalived--ipvs"&gt;The Solution: Keepalived + IPVS
&lt;/h3&gt;&lt;p&gt;By introducing Keepalived with IPVS at Layer 4, we create a single &lt;strong&gt;Virtual IP (VIP)&lt;/strong&gt;. Your clients only ever talk to this one IP address.&lt;/p&gt;
&lt;p&gt;Keepalived acts as an intelligent traffic director:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Load Balancing:&lt;/strong&gt; It splits incoming UDP and TCP port 53 DNS queries evenly across both Technitium instances using a Round Robin algorithm.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Instant Failover:&lt;/strong&gt; It continuously polls the health of each Technitium instance. If one container or DNS service crashes, Keepalived immediately removes it from the rotation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Infrastructure Redundancy:&lt;/strong&gt; Keepalived uses VRRP (Virtual Router Redundancy Protocol). If the primary load-balancing container dies entirely, the secondary container instantly assumes the VIP within milliseconds.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="schematic-overview"&gt;Schematic Overview
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[ NETWERK CLIENTS ]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(Request DNS via VIP)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ VIRTUEL IP │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 192.168.1.10 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └───────┬────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌──────┴──────┐ (Keepalived VRRP / IPVS)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ │ (Divide UDP/TCP requests via Round Robin)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼ ▼
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;┌─────────────────┐ ┌─────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ LXC Container 1 │ │ LXC Container 2 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ [MASTER] │ │ [BACKUP] │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 192.168.1.11 │ │ 192.168.1.12 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├─────────────────┤ ├─────────────────┤
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ Technitium #1 │ │ Technitium #2 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ (lo: .1.10/32) │ │ (lo: .1.10/32) │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└────────┬────────┘ └────────┬────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └──────────┬──────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ (Direct Routing - DR Mode)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; [ Direct Answer ]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; (Back to Clients)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style="margin-top: 6rem;"&gt;&lt;/div&gt;
&lt;h2 id="explanation-of-the-data-flow"&gt;Explanation of the data flow:
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;Inbound traffic: Network clients send all their DNS requests exclusively to the Virtual IP (192.168.1.10).&lt;/li&gt;
&lt;li&gt;Load Balancing: The container currently holding the MASTER role (Container 1) intercepts the traffic and distributes the requests across the two Technitium DNS backends using the Linux IPVS kernel module (Round Robin).&lt;/li&gt;
&lt;li&gt;Direct Routing (DR): Thanks to the loopback configuration (lo: 192.168.1.10/32), both containers process the packets directly and send the response straight back to the client, bypassing the load balancer. This ensures minimal latency.&lt;/li&gt;
&lt;li&gt;Failover: If Container 1 fails, Keepalived moves the VIP to Container 2 within a fraction of a second; from that point on, Container 2 handles the requests independently.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="architectural-overview"&gt;Architectural Overview
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Virtual IP (VIP):&lt;/strong&gt; &lt;code&gt;192.168.1.10&lt;/code&gt; (The single DNS address given to your clients)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Technitium Container 1 (MASTER):&lt;/strong&gt; &lt;code&gt;192.168.1.11&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Technitium Container 2 (BACKUP):&lt;/strong&gt; &lt;code&gt;192.168.1.12&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="step-1-preparing-the-proxmox-host-crucial-for-lxc"&gt;Step 1: Preparing the Proxmox Host (Crucial for LXC)
&lt;/h2&gt;&lt;p&gt;Because LXC containers share the host&amp;rsquo;s Linux kernel, they cannot load kernel modules or manipulate network routing infrastructure by default. We must prepare the Proxmox host first.&lt;/p&gt;
&lt;h3 id="1-load-ipvs-modules-on-the-host"&gt;1. Load IPVS Modules on the Host
&lt;/h3&gt;&lt;p&gt;Log into your &lt;strong&gt;Proxmox VE Host&lt;/strong&gt; via SSH and run the following commands to load the required IPVS load-balancing kernel modules:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;modprobe ip_vs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;modprobe ip_vs_rr
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To ensure these modules survive a Proxmox host reboot, append them to the host&amp;rsquo;s &lt;code&gt;/etc/modules&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ip_vs&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; /etc/modules
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ip_vs_rr&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; /etc/modules
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="2-adjust-lxc-container-permissions"&gt;2. Adjust LXC Container Permissions
&lt;/h3&gt;&lt;p&gt;You must grant your containers permission to interact with the network stack. On your Proxmox host, open the configuration files for &lt;strong&gt;both&lt;/strong&gt; containers (located at &lt;code&gt;/etc/pve/lxc/YOUR_CONTAINER_ID.conf&lt;/code&gt;) and add these lines:
Make sure the the LXC container are running in privilegded mode. Option &amp;ldquo;unprivileged: 0&amp;rdquo; in &lt;lxc-nr&gt;.conf&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lxc.apparmor.raw: mount fstype=devpts,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lxc.cgroup2.devices.allow: c 10:200 rwm
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# SAFE Capabilties (let the console alive, but give networks rights)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lxc.cap.drop: 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lxc.cap.keep: sys_admin net_admin chown dac_override fowner fsetid kill setgid setuid setpcap linux_immutable net_bind_service net_raw ipc_owner sys_chroot sys_ptrace sys_tty_config
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;em&gt;Restart both LXC containers after saving these changes.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-2-configuring-the-loopback-interface-on-the-containers"&gt;Step 2: Configuring the Loopback Interface on the Containers
&lt;/h2&gt;&lt;p&gt;We will be using &lt;strong&gt;Direct Routing (DR)&lt;/strong&gt; mode in IPVS. This is the fastest method of load balancing because incoming traffic goes through the load balancer, but the backend Technitium servers reply &lt;strong&gt;directly&lt;/strong&gt; to the client.&lt;/p&gt;
&lt;p&gt;For this to work, both containers must accept packets destined for the VIP (&lt;code&gt;192.168.1.10&lt;/code&gt;), but they must not broadcast this IP to the local network to avoid IP conflicts. We solve this by binding the VIP to the local loopback (&lt;code&gt;lo&lt;/code&gt;) interface.&lt;/p&gt;
&lt;p&gt;Inside &lt;strong&gt;both&lt;/strong&gt; Debian LXC containers, open &lt;code&gt;/etc/network/interfaces&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nano /etc/network/interfaces
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Modify the loopback configuration block to match the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;auto lo
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;iface lo inet loopback
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; up ip addr add 192.168.1.10/32 dev lo
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; down ip addr del 192.168.1.10/32 dev lo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Apply the changes instantly without a reboot:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ifdown lo &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ifup lo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="step-3-installing-and-configuring-keepalived"&gt;Step 3: Installing and Configuring Keepalived
&lt;/h2&gt;&lt;p&gt;&lt;div style="margin-top: 6rem;"&gt;&lt;/div&gt;
Now, log into both containers to install Keepalived and the IPVS administration utility:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt install -y keepalived ipvsadm
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style="margin-top: 6rem;"&gt;&lt;/div&gt;
&lt;h3 id="1-configuration-for-container-1-master"&gt;1. Configuration for Container 1 (MASTER)
&lt;/h3&gt;&lt;p&gt;Create the configuration file &lt;code&gt;/etc/keepalived/keepalived.conf&lt;/code&gt; on your first Technitium container:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;span class="lnt"&gt;45
&lt;/span&gt;&lt;span class="lnt"&gt;46
&lt;/span&gt;&lt;span class="lnt"&gt;47
&lt;/span&gt;&lt;span class="lnt"&gt;48
&lt;/span&gt;&lt;span class="lnt"&gt;49
&lt;/span&gt;&lt;span class="lnt"&gt;50
&lt;/span&gt;&lt;span class="lnt"&gt;51
&lt;/span&gt;&lt;span class="lnt"&gt;52
&lt;/span&gt;&lt;span class="lnt"&gt;53
&lt;/span&gt;&lt;span class="lnt"&gt;54
&lt;/span&gt;&lt;span class="lnt"&gt;55
&lt;/span&gt;&lt;span class="lnt"&gt;56
&lt;/span&gt;&lt;span class="lnt"&gt;57
&lt;/span&gt;&lt;span class="lnt"&gt;58
&lt;/span&gt;&lt;span class="lnt"&gt;59
&lt;/span&gt;&lt;span class="lnt"&gt;60
&lt;/span&gt;&lt;span class="lnt"&gt;61
&lt;/span&gt;&lt;span class="lnt"&gt;62
&lt;/span&gt;&lt;span class="lnt"&gt;63
&lt;/span&gt;&lt;span class="lnt"&gt;64
&lt;/span&gt;&lt;span class="lnt"&gt;65
&lt;/span&gt;&lt;span class="lnt"&gt;66
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;global_defs {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; router_id dns_lb1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; enable_script_security
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# 1. High Availability (VRRP Configuration)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vrrp_instance VI_1 {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; state MASTER
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; interface eth0 # Replace with your actual network interface
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; virtual_router_id 51
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; priority 101 # Higher priority wins the VIP
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; advert_int 1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; authentication {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; auth_type PASS
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; auth_pass A_Secure_Cluster_Password
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; virtual_ipaddress {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 192.168.1.10/24 # Your Virtual IP (VIP)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# 2. Load Balancing for DNS UDP (Port 53)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;virtual_server 192.168.1.10 53 {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; delay_loop 6
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; lb_algo rr # Round Robin distribution
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; lb_kind DR # Direct Routing mode
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; protocol UDP
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; real_server 192.168.1.11 53 {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; weight 1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; MISC_CHECK {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; misc_path &amp;#34;/usr/bin/nc -zu -w 1 127.0.0.1 53&amp;#34; # Local health check
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; misc_timeout 2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; real_server 192.168.1.12 53 {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; weight 1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; MISC_CHECK {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; misc_path &amp;#34;/usr/bin/nc -zu -w 1 192.168.1.12 53&amp;#34; # Remote health check
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; misc_timeout 2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# 3. Load Balancing for DNS TCP (Port 53)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;virtual_server 192.168.1.10 53 {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; delay_loop 6
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; lb_algo rr
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; lb_kind DR
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; protocol TCP
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; real_server 192.168.1.11 53 {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; weight 1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; TCP_CHECK {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; connect_timeout 3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; real_server 192.168.1.12 53 {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; weight 1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; TCP_CHECK {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; connect_timeout 3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div style="margin-top: 6rem;"&gt;&lt;/div&gt;
&lt;h3 id="2-configuration-for-container-2-backup"&gt;2. Configuration for Container 2 (BACKUP)
&lt;/h3&gt;&lt;p&gt;Create the exact same file on your second Technitium container, but change the &lt;code&gt;vrrp_instance&lt;/code&gt; configuration block to reflect its backup status and lower priority:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; state BACKUP
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; priority 100 # Lower than the MASTER server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style="margin-top: 6rem;"&gt;&lt;/div&gt;
&lt;h3 id="3-enable-and-start-keepalived"&gt;3. Enable and Start Keepalived
&lt;/h3&gt;&lt;p&gt;Run this command on &lt;strong&gt;both&lt;/strong&gt; containers to start the service and ensure it launches automatically at boot:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; --now keepalived
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;div style="margin-top: 6rem;"&gt;&lt;/div&gt;
&lt;h2 id="step-4-verification-and-testing"&gt;Step 4: Verification and Testing
&lt;/h2&gt;&lt;p&gt;Your high-availability cluster is now live! Let&amp;rsquo;s verify that everything is working perfectly.&lt;/p&gt;
&lt;h3 id="1-check-ip-allocation"&gt;1. Check IP Allocation
&lt;/h3&gt;&lt;p&gt;On Container 1 (MASTER), run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ip addr show eth0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You should see both its local IP (&lt;code&gt;192.168.1.11&lt;/code&gt;) and the VIP (&lt;code&gt;192.168.1.10&lt;/code&gt;). If you run the same command on Container 2, the VIP should &lt;em&gt;not&lt;/em&gt; be present.&lt;/p&gt;
&lt;h3 id="2-inspect-the-load-balancing-table"&gt;2. Inspect the Load Balancing Table
&lt;/h3&gt;&lt;p&gt;Run the following command on the container that is currently acting as the MASTER:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ipvsadm -ln
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You will see a clean routing matrix mapping your VIP to both backend servers across TCP and UDP protocols.&lt;/p&gt;
&lt;h3 id="3-simulating-a-failure"&gt;3. Simulating a Failure
&lt;/h3&gt;&lt;p&gt;To test the failover capability, stop the Keepalived service on Container 1:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl stop keepalived
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you run &lt;code&gt;ip addr show eth0&lt;/code&gt; on Container 2, you will notice that it has gracefully assumed control of the VIP (&lt;code&gt;192.168.1.10&lt;/code&gt;) in a fraction of a second. Your network clients will experience zero downtime, and DNS queries will keep resolving seamlessly.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="4-monitoring-real-time-traffic-distribution"&gt;4. Monitoring Real-Time Traffic Distribution
&lt;/h3&gt;&lt;p&gt;Once your high-availability DNS cluster is running, you will want to see it in action. The best way to monitor how Keepalived and IPVS are distributing traffic between your Technitium servers is by using the following debugging command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;watch -n 0.5 ipvsadm -Ln
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What it does&lt;/p&gt;
&lt;p&gt;This command combines two powerful Linux utilities. The ipvsadm -Ln command lists the current IP Virtual Server kernel routing table in a clean, numeric format (-n), skipping slow DNS lookups. By wrapping it in watch -n 0.5, the terminal automatically refreshes the output every half-second, providing a near-real-time view of your live network traffic.&lt;/p&gt;
&lt;p&gt;How to interpret the output
When you run this on the active MASTER container, you will see an output structured like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;IP Virtual Server version 1.2.1 (size=4096)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Prot LocalAddress:Port Scheduler Flags
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; RemoteAddress:Port Forward Weight ActiveConn InActConn
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;UDP 192.168.1.10:53 rr
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 192.168.1.11:53 Route 1 0 142 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 192.168.1.12:53 Route 1 0 139
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Key metrics to look out for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;UDP/TCP &amp;hellip; rr: Confirms that IPVS is listening on your Virtual IP (VIP) on port 53 and using the Round Robin (rr) scheduler.&lt;/li&gt;
&lt;li&gt;Forward (Route): Indicates that Direct Routing (DR) is working properly.&lt;/li&gt;
&lt;li&gt;Weight: A value of 1 means the server is healthy. If Keepalived detects that a Technitium instance is down, this weight will immediately drop to 0, removing it from rotation.&lt;/li&gt;
&lt;li&gt;InActConn (Inactive Connections): Since DNS over UDP is stateless, queries do not stay open. Instead, they quickly register as inactive connections. Watch these numbers increment evenly on both rows as clients make requests—this is visual proof that your load balancer is dividing the work perfectly!&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;div style="margin-top: 6rem;"&gt;&lt;/div&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;By coupling &lt;strong&gt;Technitium DNS&lt;/strong&gt; with &lt;strong&gt;Keepalived&lt;/strong&gt; and &lt;strong&gt;IPVS&lt;/strong&gt;, you eliminate the classic pitfalls of primary/secondary DNS configurations. You get an enterprise-grade setup that guarantees absolute uptime, linear traffic distribution, and instantaneous failover management—all running within lightweight, low-overhead Proxmox LXC containers.&lt;/p&gt;
&lt;hr&gt;</description></item></channel></rss>