<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>DNS on Not /.well-known</title><link>https://rogena.me/tags/dns/</link><description>Recent content in DNS on Not /.well-known</description><generator>Hugo</generator><language>en-us</language><copyright>Jason Rogena</copyright><lastBuildDate>Sun, 12 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://rogena.me/tags/dns/index.xml" rel="self" type="application/rss+xml"/><item><title>Running My Home Lab Using DNS-SD</title><link>https://rogena.me/posts/2026-07-12-running-my-home-lab-using-dns-sd/</link><pubDate>Sun, 12 Jul 2026 00:00:00 +0000</pubDate><guid>https://rogena.me/posts/2026-07-12-running-my-home-lab-using-dns-sd/</guid><description>&lt;blockquote&gt;
&lt;p&gt;This post has NOT been generated using AI.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Like most of those who I believe would be reading this blog, I run my own home lab. My setup has, over the years, evolved from an old laptop running just my media centre, only accessible on my home network. Today, it is a more elaborate setup with containers running on two hosts on my local network, a third host running local LLMs, and a fourth (thin) host in the cloud running a reverse proxy. The reverse proxy forwards requests to services running in the hosts on my local network, allowing me to access some services from anywhere. I like the setup. In essence, it is quite simple. All I have to deal with is a container engine (I use Podman Quadlet) and my reverse proxy, outside the actual services deployed in the lab. I have avoided jumping into the Kubernetes (or similar) bandwagon, as I still don&amp;rsquo;t think the overhead is justified as yet.&lt;/p&gt;
&lt;p&gt;It being a home lab means I quite often bring up and take down services. One big pain point for me was having to SSH into my cloud host to update the reverse proxy configuration every time I&amp;rsquo;d want to expose or stop exposing a service to the internet. So last year, while in between jobs, I started exploring how I could auto-configure which services to expose to the internet. I was interested in mechanisms that would be agnostic to whatever container engine I use (having moved from systemd-nspawn to Podman, and afraid I&amp;rsquo;d do a similar switch soon after).&lt;/p&gt;
&lt;h2 id="srv-for-the-win"&gt;SRV for the Win&lt;/h2&gt;
&lt;p&gt;DNS was coming out on top for this kind of container-engine-agnostic mechanism of service discovery. The rough idea was I would configure my reverse proxy to accept requests from any subdomain in a wildcard domain (let&amp;rsquo;s say &lt;code&gt;*.apps.rogena.me&lt;/code&gt;). The reverse proxy would extract whatever subdomain was being hit, let&amp;rsquo;s say &lt;code&gt;home-assistant&lt;/code&gt; in &lt;code&gt;home-assistant.apps.rogena.me&lt;/code&gt;, and forward the request to a service running inside whatever container IP address either &lt;code&gt;home-assistant.host1.lan&lt;/code&gt; or &lt;code&gt;home-assistant.host2.lan&lt;/code&gt; resolves to.&lt;/p&gt;
&lt;p&gt;Two things I had to figure out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Typical DNS record types (A and AAAA) would only allow discovering the IP address for the container, and not the port the service is running on in the container.&lt;/li&gt;
&lt;li&gt;What would load-balancing across multiple containers exposing the same service look like?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;SRV is a lesser-known DNS record type that allows you to discover both the IP address and port a service is running on. Additionally, you can encode, as part of an SRV record, the priority and weight to use for the returned host-port pairs when trying to decide which one to use if multiple host-port pairs are returned when a query is made for an SRV record. The priority field encodes &amp;ldquo;who to send the request to first&amp;rdquo;. A lower value for this field indicates a higher priority. Under normal operation, requests should only be sent to the host-port pair with the lowest priority value. The weight field encodes how to split load amongst the host-port pairs that have the lowest priority value. A higher weight indicates a host-port pair can take more load. With these two fields, you can encode your fail-over as well as load-balancing strategy.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# dig home-assistant._http._tcp.host1.lan. SRV
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;; &amp;lt;&amp;lt;&amp;gt;&amp;gt; DiG 9.18.39-0ubuntu0.22.04.4-Ubuntu &amp;lt;&amp;lt;&amp;gt;&amp;gt; home-assistant._http._tcp.host1.lan. SRV
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; global options: +cmd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; Got answer:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; -&amp;gt;&amp;gt;HEADER&amp;lt;&amp;lt;- opcode: QUERY, status: NOERROR, id: 48215
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; OPT PSEUDOSECTION:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;; EDNS: version: 0, flags:; udp: 65494
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; QUESTION SECTION:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;home-assistant._http._tcp.host1.lan. IN SRV
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; ANSWER SECTION:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;home-assistant._http._tcp.host1.lan. 60 IN SRV 0 77 8123 0.home-assistant.host1.lan.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; ADDITIONAL SECTION:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;0.home-assistant.host1.lan. 60 IN A 192.168.10.58
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; Query time: 44 msec
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; WHEN: Sun Jul 12 20:11:31 UTC 2026
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; MSG SIZE rcvd: 169
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The above shows an example dig for an SRV record &lt;code&gt;home-assistant._http._tcp.host1.lan.&lt;/code&gt;. &lt;code&gt;_http&lt;/code&gt; and &lt;code&gt;_tcp&lt;/code&gt; in this example encode the service name and protocol. In the answer, &lt;code&gt;0 77&lt;/code&gt; shows the priority and weight, while &lt;code&gt;8123 0.home-assistant.host1.lan.&lt;/code&gt; shows the port and the A record with the IP address of the container running Home Assistant.&lt;/p&gt;
&lt;p&gt;Great success! I&amp;rsquo;m able to discover which container IP and port I should hit to access a service. The reverse proxy can, in theory, run a DNS SRV query for &lt;code&gt;home-assistant._http._tcp.host1.lan.&lt;/code&gt; and &lt;code&gt;home-assistant._http._tcp.host2.lan.&lt;/code&gt; when a user tries accessing &lt;code&gt;https://home-assistant.apps.rogena.me&lt;/code&gt;. In a section below, I explain exactly how I configured Caddy (my reverse proxy).&lt;/p&gt;
&lt;p&gt;I couldn&amp;rsquo;t find a container-engine-agnostic DNS server that exposes SRV records for containers running on hosts, so I wrote &lt;a href="https://github.com/jasonrogena/container-dns"&gt;container-dns&lt;/a&gt;. container-dns runs inside host1 and host2. With container-dns, all I need to do is to configure an appropriate hostname for the container and add the http port for the service in the container&amp;rsquo;s &lt;code&gt;/etc/services&lt;/code&gt; file. With &lt;code&gt;home-assistant._http._tcp.host1.lan.&lt;/code&gt;, I set the hostname for the container as &lt;code&gt;home-assistant&lt;/code&gt; and add a line in &lt;code&gt;/etc/services&lt;/code&gt; in the container with &lt;code&gt;http 8123/tcp&lt;/code&gt; (the name of the service and the TCP port the service is running on).&lt;/p&gt;
&lt;h2 id="discovering-extra-service-metadata"&gt;Discovering Extra Service Metadata&lt;/h2&gt;
&lt;p&gt;More recently, I have explored using DNS to solve a different discovery pain point with my home lab. I wanted to auto-enable uptime monitoring for all the services I expose to the internet. Naturally, some services running in my home lab are now being used by people other than me. I&amp;rsquo;d love to know when these services are down before they do.&lt;/p&gt;
&lt;p&gt;I explored how to auto-discover:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Which services to do uptime monitoring for&lt;/li&gt;
&lt;li&gt;Which domain names these services are running under&lt;/li&gt;
&lt;li&gt;What path in the domain is most appropriate to monitor&lt;/li&gt;
&lt;li&gt;What HTTP status code and content I should expect to be returned if a service is healthy&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Turns out the SRV record mechanism container-dns supports is part of a larger specification called &lt;a href="https://www.rfc-editor.org/info/rfc6763/"&gt;DNS-SD&lt;/a&gt;. DNS-SD also specifies how to enumerate which services are deployed under a domain as well as extra metadata for a service. I would use these to auto-discover which services to do uptime monitoring for, as well as how to monitor them.&lt;/p&gt;
&lt;p&gt;DNS-SD works using a combination of the PTR, SRV, and TXT record types. A PTR record points a domain name to another domain name, while a TXT record allows you to attach arbitrary text (in most cases key-value pairs) to a domain name. I&amp;rsquo;ll attempt to explain how DNS-SD does service discovery in my setup using a chain of DNS queries. To discover all services running in host1, you would query a PTR record (&lt;code&gt;_services._dns-sd._udp&lt;/code&gt; is documented in the DNS-SD spec):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# dig _services._dns-sd._udp.host1.lan PTR
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; Truncated, retrying in TCP mode.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;; &amp;lt;&amp;lt;&amp;gt;&amp;gt; DiG 9.18.39-0ubuntu0.22.04.4-Ubuntu &amp;lt;&amp;lt;&amp;gt;&amp;gt; _services._dns-sd._udp.host1.lan PTR
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; global options: +cmd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; Got answer:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; -&amp;gt;&amp;gt;HEADER&amp;lt;&amp;lt;- opcode: QUERY, status: NOERROR, id: 5303
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; flags: qr rd ra; QUERY: 1, ANSWER: 50, AUTHORITY: 0, ADDITIONAL: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; OPT PSEUDOSECTION:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;; EDNS: version: 0, flags:; udp: 65494
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; QUESTION SECTION:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;_services._dns-sd._udp.host1.lan. IN PTR
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; ANSWER SECTION:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;_services._dns-sd._udp.host1.lan. 59 IN PTR _http._tcp.host1.lan.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;_services._dns-sd._udp.host1.lan. 59 IN PTR _ssh._tcp.host1.lan.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;_services._dns-sd._udp.host1.lan. 59 IN PTR _postgresql._tcp.host1.lan.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Looks like there is &lt;code&gt;http&lt;/code&gt;, &lt;code&gt;ssh&lt;/code&gt;, and &lt;code&gt;postgresql&lt;/code&gt;. For &lt;code&gt;http&lt;/code&gt;, you can do a PTR query to check which instances expose the service by querying the PTR record:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# dig _http._tcp.host1.lan PTR
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;; &amp;lt;&amp;lt;&amp;gt;&amp;gt; DiG 9.18.39-0ubuntu0.22.04.4-Ubuntu &amp;lt;&amp;lt;&amp;gt;&amp;gt; _http._tcp.host1.lan PTR
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; global options: +cmd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; Got answer:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; -&amp;gt;&amp;gt;HEADER&amp;lt;&amp;lt;- opcode: QUERY, status: NOERROR, id: 27672
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; flags: qr rd ra; QUERY: 1, ANSWER: 15, AUTHORITY: 0, ADDITIONAL: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; OPT PSEUDOSECTION:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;; EDNS: version: 0, flags:; udp: 65494
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; QUESTION SECTION:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;_http._tcp.host1.lan. IN PTR
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; ANSWER SECTION:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;_http._tcp.host1.lan. 60 IN PTR yarr._http._tcp.host1.lan.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;_http._tcp.host1.lan. 60 IN PTR home-assistant._http._tcp.host1.lan.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;_http._tcp.host1.lan. 60 IN PTR grafana._http._tcp.host1.lan.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; Query time: 44 msec
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; WHEN: Sun Jul 12 21:28:25 UTC 2026
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; MSG SIZE rcvd: 396
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Looks like an http service is available in &lt;code&gt;yarr&lt;/code&gt;, &lt;code&gt;home-assistant&lt;/code&gt;, and &lt;code&gt;grafana&lt;/code&gt;. Note how the record &lt;code&gt;home-assistant._http._tcp.host1.lan.&lt;/code&gt; matches the SRV record we covered above. We can also get the metadata for the Home Assistant http service by querying a TXT record (corresponding to the SRV record for the service):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# dig home-assistant._http._tcp.host1.lan. TXT
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;; &amp;lt;&amp;lt;&amp;gt;&amp;gt; DiG 9.18.39-0ubuntu0.22.04.4-Ubuntu &amp;lt;&amp;lt;&amp;gt;&amp;gt; home-assistant._http._tcp.host1.lan. TXT
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; global options: +cmd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; Got answer:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; -&amp;gt;&amp;gt;HEADER&amp;lt;&amp;lt;- opcode: QUERY, status: NOERROR, id: 40093
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; OPT PSEUDOSECTION:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;; EDNS: version: 0, flags:; udp: 65494
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; QUESTION SECTION:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;home-assistant._http._tcp.host1.lan. IN TXT
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; ANSWER SECTION:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;home-assistant._http._tcp.host1.lan. 60 IN TXT &amp;#34;domain=home-assistant.apps.rogena.me&amp;#34; &amp;#34;healthz=/auth/authorize&amp;#34; &amp;#34;okstatus=200&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; Query time: 44 msec
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; WHEN: Sun Jul 12 21:33:00 UTC 2026
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; MSG SIZE rcvd: 163
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The answer for the TXT record contains several metadata fields: domain, healthz, and okstatus. I can use these to configure how to do uptime monitoring for Home Assistant (i.e., attempt to reach &amp;ldquo;&lt;a href="https://home-assistant.apps.rogena.me/auth/authorize%22"&gt;https://home-assistant.apps.rogena.me/auth/authorize&amp;quot;&lt;/a&gt; and check if it returns the HTTP status code 200).&lt;/p&gt;
&lt;p&gt;I extended container-dns to check in each of the containers for the &lt;code&gt;/etc/container-dns/txt&lt;/code&gt; file and expose the data in that file in the TXT record for the services the container runs. For Home Assistant, this is how the file looks:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;[8123/tcp]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;domain&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;home-assistant.apps.rogena.me&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;healthz&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;/auth/authorize&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;okstatus&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;200&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I wrote &lt;a href="https://github.com/jasonrogena/upwatch"&gt;upwatch&lt;/a&gt;, an uptime monitoring service that talks DNS-SD. I don&amp;rsquo;t need to directly configure it to add or remove a monitor for a service. I just have to make sure the &lt;code&gt;/etc/container-dns/txt&lt;/code&gt; file in the container running the service has the right fields configuring how to monitor the service.&lt;/p&gt;
&lt;h2 id="configuring-caddy"&gt;Configuring Caddy&lt;/h2&gt;
&lt;p&gt;I use Caddy as my reverse proxy exposing the services I run in my home lab to the internet. This is how my Caddyfile looks:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;*.apps.rogena.me {
 tls {
 dns cloudflare &amp;lt;REDACTED&amp;gt;
 }
 reverse_proxy {
 dynamic multi {
 srv {labels.3}._http._tcp.host1.lan {
 refresh 15s
 grace_period 2m
 },
 srv {labels.3}._http._tcp.host2.lan {
 refresh 15s
 grace_period 2m
 }
 }
 }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Caddy calls the DNS resolver on the host it is running on. I have configured the resolver (systemd-resolved) to forward DNS queries for host1 and host2 to the corresponding instance of container-dns.&lt;/p&gt;</description></item></channel></rss>