STFN

How to set up subdomains in the homelab with PiHole and Caddy

10 minutes

I have a homelab in which I run several self-hosted services (more on it here). The services are deployed as Docker containers, and each of them exposes a different port. Up until now I have been using the port number to access them, for example homelab.local:3002 would lead me to Grafana. The landing page for the homelab is served by Homepage which is bound to port 80, the default port for HTTP. Thanks to that, I can just access homelab.local and use the links presented there to access the services without needing to remember their ports.

Screenshot
of my homepage. More detailed description in the blog post.The dashboard

But this gets tiring quickly when you want to access the services directly. And well, for a moment my Homelab felt too complete and I wanted to check out some new configuration and tooling. I wanted to have subdomains, so that instead of going to homelab.local:3002 I could just visit grafana.homelab.local.

Here comes DNS

DNS is a tool that translates IP addresses into human-readable URLs. Thanks to DNS, you can ask your browser to access fosstodon.org and not a bunch of numbers making an IP address.

To make the subdomains work, we’ll need two things: an HTTP server than can do reverse proxying, and a local DNS server. The DNS server will tell your computer how to reach the homelab, and the reverse proxy will distribute the incoming traffic between the different services. For the first part, we’ll use Caddy, and for the second one there is something that a lot of homelabbers is using, but not directly for this feature: Pi-hole.

Caddy setup

Why Caddy you ask? I chose it because I’ve seen good reviews of it in several places, and most of all because I wanted to try a tool I have not used before. For this blog I am using nginx, so I looked for alternatives, and Caddy was high on the list of recommended software.

The installation instructions for Caddy can be found in the Caddy docs. In my homelab I am using Debian Bookworm, so I installed it by adding the Caddy repository to my software sources, and then installing it with apt.

This way after installation I have a systemd service which can run a daemon in the background.

Investigating the service file (/lib/systemd/system/caddy.service) shows that Caddy is storing its configuration in the Caddyfile at /etc/caddy/Caddyfile, and this is the file that we’ll need to edit.

sudo nano /etc/caddy/Caddyfile

The Caddyfile will have a default configuration of serving static webpages. We won’t need it, you can just remove it or comment it out. What we will need is a reverse proxy, so, all we need in the Caddyfile is the setup for the reverse proxy, anything else can be let at their defaults values.

A reverse proxy is a service that accepts requests from the client, and forwards them to another service living on the server. There can be several of those services running together on the server, and Caddy will distribute traffic between them based on the URL of the request.

Here is how my Caddyfile looks like:

http://homelab.local {
reverse_proxy :3001
}

http://photo.homelab.local {
reverse_proxy :2342
}

http://music.homelab.local {
reverse_proxy :4533
}

http://rss.homelab.local {
reverse_proxy :8090
}

http://jellyfin.homelab.local {
reverse_proxy :8096
}

This tells Caddy that if it receives a request from the client’s browser to access the URL homelab.local, it needs to forward it to service running at port 3001, which is my Homepage instance. But if the client’s browser requests the URL rss.homelab.local, it needs to forward it to the service running at port 8090, which in my case is FreshRSS.

After applying the changes, we need to restart the Caddy service

sudo systemctl restart caddy.service

This part is done, and now it is the time to move to the second part of the setup.

Pi-hole setup

I assume your client device, the one from which you will be connecting to the homelab, is already configured to use Pi-hole. If not, go through the post-installation steps.

Now our homelab knows how to distribute the requests coming to it based on the URL, but there is one big problem. Our client device, the one from which you are connecting to the homelab to get the RSS feeds or music, does not know that requesting rss.homelab.local or music.homelab.local must lead to the same computer, to the homelab. Remember, as we discussed in the beginning, the client uses a DNS server to be told which address means which computer.

The base url, homelab.local, should work out of the box due to local DNS resolution, (which is an ever more black box for me), but the subdomains will not work unless we specifically setup the DNS server to tell the clients that the subdomains are the same computer as the base one.

And here comes Pi-hole. Pi-hole adblocking functionality is based on DNS.

For example, when you visit a page, like onlinenewsthatmakeyouangry.com, the page tells your browser to also pull images from ads.onlinenewsthatmakeyouangry.com, and Pi-hole will see that those are ads, and will not return an IP address for them. The ads will not be fetched and you will not see them. That’s Pi-hole in two sentences.

Pi-hole allows setting up custom DNS entries, and this what we will use to point the client to our homelab subdomains. Local DNS is the menu entry that we need. The screenshot shows how it should be configured. Remember to put the IP address of your homelab.

A screenshot
of my Pihole admin page. The page shows the Local DNS submenu. Under the 'List
of local DNS domains' there are five entries showing the different subdomains I
have configured, each leading to the same IP address

Most probably you will need to disconnect your client device and connect to your network again to make the changes work.

If everything was configured, the subdomains should now work. Try it out!

HTTP or HTTPS?

In the Caddyfile I provided the URLs starting with http://. One of the big features of Caddy is automatic configuration of HTTPS, so why am I using HTTP?

I do not expose my homelab to the Internet, and I do not have a public domain for it. For such domains Caddy creates a self-signed SSL certificate, which the browsers find suspicious and warn you before accessing pages with such “shady” certificates. Again, my homelab is not available from outside my network, so I don’t care about HTTPS, and I don’t want to see a warning every time I visit my local server, therefore I am telling Caddy specifically to only use HTTP.

If you have a homelab living in the wide open Internet and it has a public domain, then you can remove the http:// prefixes from the Caddyfile, and Caddy will generate a proper SSL certificate for your server.

And that’s it, your homelab is now on a totally new level! Thanks for reading!

If you enjoyed this post, please consider helping me make new projects by supporting me on the following crowdfunding sites: