<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Tech on Welcome to my photography blog</title><link>https://pvdplaat.nl/en/tags/tech/</link><description>Recent content in Tech on Welcome to my photography blog</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Sun, 17 May 2026 13:00:43 +0200</lastBuildDate><atom:link href="https://pvdplaat.nl/en/tags/tech/index.xml" rel="self" type="application/rss+xml"/><item><title>Privacy by Routing DNS Traffic Through a VPN Tunnel</title><link>https://pvdplaat.nl/en/blog/privacy-dns-over-vpn/</link><pubDate>Sun, 17 May 2026 13:00:43 +0200</pubDate><guid>https://pvdplaat.nl/en/blog/privacy-dns-over-vpn/</guid><description>&lt;img src="https://pvdplaat.nl/images/2026/routerboard.webp" alt="Featured image of post Privacy by Routing DNS Traffic Through a VPN Tunnel" /&gt;&lt;h1 id="privacy-by-routing-dns-traffic-through-a-vpn-tunnel-on-a-mikrotik-router"&gt;Privacy by routing DNS traffic through a VPN Tunnel on a Mikrotik router
&lt;/h1&gt;&lt;h3 id="why-dns-privacy-still-matters-in-2026"&gt;Why DNS Privacy Still Matters in 2026
&lt;/h3&gt;&lt;p&gt;Most people think that when they browse the internet using HTTPS, their online activities are completely private. Unfortunately, that is not entirely true.&lt;/p&gt;
&lt;p&gt;HTTPS encrypts the content of your communication with a website. Your internet provider cannot see the pages you read, the forms you submit, or the passwords you enter. However, before your browser can connect to a website, it first needs to know the IP address of that website.&lt;/p&gt;
&lt;p&gt;This is where DNS comes into play.&lt;/p&gt;
&lt;p&gt;DNS (Domain Name System) acts as the internet&amp;rsquo;s phonebook. When you visit a website such as &lt;code&gt;www.example.com&lt;/code&gt;, your device performs a DNS query to find the corresponding IP address.&lt;/p&gt;
&lt;p&gt;Traditionally, these DNS queries are sent in plain text over the internet. Anyone between you and the DNS server can potentially see which domains you are looking up.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="the-privacy-problem"&gt;The Privacy Problem
&lt;/h3&gt;&lt;p&gt;Even when all your web traffic is protected by HTTPS, DNS requests can reveal a surprising amount of information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Websites you visit&lt;/li&gt;
&lt;li&gt;Applications you use&lt;/li&gt;
&lt;li&gt;Smart home devices in your network&lt;/li&gt;
&lt;li&gt;Streaming services you access&lt;/li&gt;
&lt;li&gt;Business services and cloud platforms you connect to&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In many cases, DNS traffic creates a detailed profile of your online behavior.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="isnt-dns-over-https-the-solution"&gt;Isn&amp;rsquo;t DNS over HTTPS the Solution?
&lt;/h3&gt;&lt;p&gt;DNS over HTTPS (DoH) is certainly an improvement.&lt;/p&gt;
&lt;p&gt;Instead of sending DNS requests in plain text on port 53, DNS queries are encapsulated within HTTPS traffic on port 443.&lt;/p&gt;
&lt;p&gt;This provides protection against:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Local network snooping&lt;/li&gt;
&lt;li&gt;Public Wi-Fi monitoring&lt;/li&gt;
&lt;li&gt;Basic ISP DNS inspection&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, DoH does not make you invisible.&lt;/p&gt;
&lt;p&gt;Your ISP can still see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The destination IP address you connect to&lt;/li&gt;
&lt;li&gt;The DNS provider you use&lt;/li&gt;
&lt;li&gt;Traffic patterns&lt;/li&gt;
&lt;li&gt;Connection metadata&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Depending on the implementation, technologies such as SNI (Server Name Indication) or traffic analysis may still reveal information about the services being accessed.&lt;/p&gt;
&lt;p&gt;In short:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DNS over HTTPS encrypts the DNS query itself, but not necessarily all metadata surrounding the connection.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="a-different-approach-route-dns-through-a-vpn"&gt;A Different Approach: Route DNS Through a VPN
&lt;/h2&gt;&lt;p&gt;A stronger privacy model is to route all DNS traffic through a VPN tunnel.&lt;/p&gt;
&lt;p&gt;In this setup:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Internal DNS servers perform DNS lookups.&lt;/li&gt;
&lt;li&gt;DNS traffic is marked by the router.&lt;/li&gt;
&lt;li&gt;The marked traffic is sent through a WireGuard VPN tunnel.&lt;/li&gt;
&lt;li&gt;The ISP only sees encrypted WireGuard traffic.&lt;/li&gt;
&lt;li&gt;External DNS providers only see the VPN endpoint address.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This significantly reduces the visibility of DNS activity to your internet provider.&lt;/p&gt;




 
 
 &lt;img src="https://pvdplaat.nl/images/2026/routerboard_hu_d9dea9825a06fb9b.png" alt="Mikrotik Routerboard" width="300" height="146"&gt;


&lt;h2 id="my-environment"&gt;My Environment
&lt;/h2&gt;&lt;p&gt;I use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MikroTik Router with RouterOS 7.20.6&lt;/li&gt;
&lt;li&gt;WireGuard VPN&lt;/li&gt;
&lt;li&gt;ProtonVPN (or other VPN provider)&lt;/li&gt;
&lt;li&gt;Multiple Pi-hole DNS servers, running on my Proxmox homelab server&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal is simple:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Only DNS traffic should travel through the VPN tunnel.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;All normal internet traffic continues to use the regular WAN connection.&lt;/p&gt;
&lt;p&gt;This approach provides additional privacy without forcing all users and applications through the VPN.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-1-create-an-address-list"&gt;Step 1: Create an Address List
&lt;/h2&gt;&lt;p&gt;First, create an address list containing all internal DNS servers.&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;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/ip/firewall/address-list
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;add address=192.168.1.10 comment=Pi-Hole1 list=&amp;#34;DNS Servers&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;add address=192.168.1.11 comment=Pi-Hole2 list=&amp;#34;DNS Servers&amp;#34;
&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;h3 id="what-does-this-do"&gt;What does this do?
&lt;/h3&gt;&lt;p&gt;This creates a group called &lt;strong&gt;DNS Servers&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Instead of creating firewall rules for every DNS server individually, we can simply reference this address list.&lt;/p&gt;
&lt;p&gt;Advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Easier management&lt;/li&gt;
&lt;li&gt;Better scalability&lt;/li&gt;
&lt;li&gt;Cleaner configuration&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="step-2-mark-dns-traffic"&gt;Step 2: Mark DNS Traffic
&lt;/h2&gt;&lt;p&gt;The next step is to identify DNS traffic generated by these servers and mark it for special routing.&lt;/p&gt;
&lt;h3 id="udp-port-53"&gt;UDP Port 53
&lt;/h3&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;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;add action=mark-routing chain=prerouting \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; comment=&amp;#34;DNS traffic (UDP) via WG-ProtonVPN&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; dst-port=53 \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; new-routing-mark=to-ProtonVPN \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; passthrough=no \
&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; src-address-list=&amp;#34;DNS Servers&amp;#34;
&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;h3 id="explanation"&gt;Explanation
&lt;/h3&gt;&lt;p&gt;This rule says:&lt;/p&gt;
&lt;p&gt;&amp;ldquo;If traffic originates from one of the DNS servers and uses UDP port 53, mark it with routing mark &lt;code&gt;to-ProtonVPN&lt;/code&gt;.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;UDP 53 is the traditional DNS protocol.&lt;/p&gt;
&lt;p&gt;The parameter:&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;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;passthrough=no
&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;means processing stops after the packet is marked.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="udp-port-853"&gt;UDP Port 853
&lt;/h3&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;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;add action=mark-routing chain=prerouting \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; comment=&amp;#34;DNS traffic (UDP) via WG-ProtonVPN&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; dst-port=853 \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; new-routing-mark=to-ProtonVPN \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; passthrough=no \
&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; src-address-list=&amp;#34;DNS Servers&amp;#34;
&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;h3 id="explanation-1"&gt;Explanation
&lt;/h3&gt;&lt;p&gt;Port 853 is commonly used by encrypted DNS technologies.
This ensures those DNS requests are also routed through the VPN.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="tcp-port-53"&gt;TCP Port 53
&lt;/h3&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;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;add action=mark-routing chain=prerouting \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; comment=&amp;#34;DNS traffic (TCP) via WG-ProtonVPN&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; dst-port=53 \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; new-routing-mark=to-ProtonVPN \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; passthrough=no \
&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; src-address-list=&amp;#34;DNS Servers&amp;#34;
&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;h3 id="explanation-2"&gt;Explanation
&lt;/h3&gt;&lt;p&gt;Although most DNS traffic uses UDP, larger responses may fall back to TCP.&lt;/p&gt;
&lt;p&gt;Examples include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Large DNS responses&lt;/li&gt;
&lt;li&gt;DNS zone transfers&lt;/li&gt;
&lt;li&gt;Certain DNSSEC operations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This rule ensures TCP-based DNS traffic is also protected.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="tcp-port-853"&gt;TCP Port 853
&lt;/h3&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;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;add action=mark-routing chain=prerouting \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; comment=&amp;#34;DNSSEC traffic (TCP) via WG-ProtonVPN&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; dst-port=853 \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; new-routing-mark=to-ProtonVPN \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; passthrough=no \
&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; src-address-list=&amp;#34;DNS Servers&amp;#34;
&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;h3 id="explanation-3"&gt;Explanation
&lt;/h3&gt;&lt;p&gt;This captures encrypted &amp;lsquo;DNS of TLS&amp;rsquo; (DoT) traffic using TCP on port 853.&lt;/p&gt;
&lt;p&gt;Again, the traffic receives the routing mark and is directed toward the VPN.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="tcp-port-443"&gt;TCP Port 443
&lt;/h3&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;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;add action=mark-routing chain=prerouting \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; comment=&amp;#34;DNS-HTTPS traffic (TCP) via WG-ProtonVPN&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; dst-port=443 \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; new-routing-mark=to-ProtonVPN \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; passthrough=no \
&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; src-address-list=&amp;#34;DNS Servers&amp;#34;
&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;h3 id="explanation-4"&gt;Explanation
&lt;/h3&gt;&lt;p&gt;This is the rule that captures DNS over HTTPS (DoH).&lt;/p&gt;
&lt;p&gt;Most DoH providers use HTTPS over TCP port 443.&lt;/p&gt;
&lt;p&gt;Because the traffic originates from the DNS servers in our address list, we can reasonably assume this HTTPS traffic is DNS-related and should be sent through the VPN.&lt;/p&gt;
&lt;p&gt;This prevents DNS-over-HTTPS requests from bypassing the privacy tunnel.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-3-create-a-dedicated-route"&gt;Step 3: Create a Dedicated Route
&lt;/h2&gt;&lt;p&gt;Now we create a routing table that uses the WireGuard tunnel.&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;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/ip route
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;add disabled=no \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; distance=1 \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; dst-address=0.0.0.0/0 \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; gateway=wg2-ProtonVPN \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; routing-table=to-ProtonVPN \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; scope=30 \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; target-scope=10
&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;h3 id="explanation-5"&gt;Explanation
&lt;/h3&gt;&lt;p&gt;This route says:&lt;/p&gt;
&lt;p&gt;&amp;ldquo;When traffic arrives with routing mark &lt;code&gt;to-ProtonVPN&lt;/code&gt;, send it through the WireGuard interface &lt;code&gt;wg2-ProtonVPN&lt;/code&gt;.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Only marked traffic uses this route.&lt;/p&gt;
&lt;p&gt;All other traffic follows the normal routing table.&lt;/p&gt;
&lt;p&gt;This is what makes selective VPN routing possible.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-4-routing-rule"&gt;Step 4: Routing Rule
&lt;/h2&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-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/routing rule
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;add action=lookup \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; comment=&amp;#34;Route DNS traffic over ProtonVPN&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; disabled=no \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; routing-mark=to-ProtonVPN \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; table=to-ProtonVPN
&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;h3 id="explanation-6"&gt;Explanation
&lt;/h3&gt;&lt;p&gt;This rule tells RouterOS to perform a route lookup in the &lt;code&gt;to-ProtonVPN&lt;/code&gt; routing table whenever packets carry the routing mark &lt;code&gt;to-ProtonVPN&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;On modern RouterOS versions, routing marks and routing tables often work directly together without requiring additional routing rules. Depending on your RouterOS design and version, this rule may not be necessary.&lt;/p&gt;
&lt;p&gt;Always test routing behavior after enabling or disabling routing rules.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="what-does-my-isp-see"&gt;What Does My ISP See?
&lt;/h2&gt;&lt;p&gt;Without this setup:&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;/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;DNS Server ---&amp;gt; Internet DNS Provider
&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;The ISP can observe DNS traffic and metadata.&lt;/p&gt;
&lt;p&gt;With this setup:&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;/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;DNS Server ---&amp;gt; WireGuard Tunnel ---&amp;gt; VPN Provider ---&amp;gt; DNS Provider
&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;The ISP only sees:&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;/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;Encrypted WireGuard Traffic
&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;The DNS requests themselves travel inside the VPN tunnel.&lt;/p&gt;
&lt;p&gt;This significantly improves privacy.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="final-thoughts"&gt;Final Thoughts
&lt;/h2&gt;&lt;p&gt;Privacy on the internet is not a single technology.&lt;/p&gt;
&lt;p&gt;HTTPS protects website content.&lt;/p&gt;
&lt;p&gt;DNS over HTTPS protects DNS queries.&lt;/p&gt;
&lt;p&gt;WireGuard protects traffic between your router and the VPN endpoint.&lt;/p&gt;
&lt;p&gt;By combining these technologies and routing DNS traffic through a dedicated WireGuard tunnel on MikroTik RouterOS, you create multiple layers of privacy.&lt;/p&gt;
&lt;p&gt;The result is a simple but effective setup:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Internal DNS servers remain local.&lt;/li&gt;
&lt;li&gt;DNS traffic is encrypted.&lt;/li&gt;
&lt;li&gt;DNS traffic is hidden from the ISP.&lt;/li&gt;
&lt;li&gt;Normal internet traffic can continue using the regular WAN connection.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For home labs and privacy-conscious MikroTik users, selective DNS routing over WireGuard offers an elegant balance between privacy, performance, and simplicity.&lt;/p&gt;
&lt;hr&gt;</description></item><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>