This is the last installment in a three-part series.
Phase 1: Initial Docker Network Setup
My homelab journey started with simple networking: a router and a server running Docker. Ports were exposed directly using Docker's default networking, which meant zero security and no remote access. Although no explicit configuration was required to put a server up, it was getting tedious remembering those port numbers. In a nutshell, the configurations looked like this:
The above setup lasted for about 2 months. Constant changing and updating of my mental model was getting tough, prompting the need for a more robust setup. Next I adopted Nginx Proxy Manager, and employed a previously deployed Pi-hole for DNS. A local DNS name was allotted to the server and subsequent names for the services were added as CNAME records to that domain. On the Nginx side, the requests were being routed to the correct container based on the requested domain. This reduced the mental hurdle of remembering exact port numbers, though the applications were still accessible on those open ports.
This deficiency was solved by assigning a common Docker network. As mentioned in this article, I have switched to using docker-compose for deployment and management of my services. The default network infra-default was designated as the "carrier" network to connect the proxy to all the running services. No ports were opened to the host network, reducing port conflicts. This resulted in the following network diagram:
Although this reduced the mental burden of remembering ports and IP addresses, it had some downsides too. The biggest downside was the growing management burden: for every new service, I had to manually assign CNAMEs and configure routing rules in Nginx Proxy Manager.
This setup was my longest-standing architecture. As the rate of adding new services decreased, changing the setup was no longer a priority for me. This changed when a new requirement came into the picture, changing the whole networking architecture.
Phase 2: Remote Access with Tailscale and Traefik
Suddenly, I needed a router, and being able to access the server outside the LAN became more important. I added a TP-Link USB dongle to act as the second network interface, and Tailscale was introduced to the setup.
Tailscale took up the burden of connecting different devices and forming a Virtual LAN; devices in the tailnet behaved as if they were on the same local network. To address the router requirement, the Raspberry Pi's internal Wi-Fi adapter was set to Hotspot mode, relaying traffic through the USB adapter. This allowed fast connections to services when at home, while relying on the tailnet when client devices were not on the LAN. The switching of network paths was handled automatically by Tailscale. This simplification allowed me to point everything to the tailnet IP and leave the rest of the networking to Tailscale itself. Alongside this, the addition of Traefik greatly simplified my setup and reduced management overhead. However, moving to Tailscale added a strict requirement: internet access is needed to initiate the connection. This resulted in my final network setup as of this date.