Run services
Add a private HTTPS proxy
Terminate HTTPS at a reverse proxy and keep the application container bound to loopback.
10 minute lesson
Right now the service answers on 127.0.0.1:8081 and nothing on the LAN can reach it. That was deliberate. Now we add the one deliberate entry point: a reverse proxy that terminates HTTPS on the LAN and forwards to the loopback port. The proxy gives users one named HTTPS entry point while the application remains private on its local port.
Caddy fits this job well because it can act as its own certificate authority for private names. Public certificate authorities can’t issue certificates for homeserver.lab.test — the name doesn’t exist on the Internet — so Caddy creates a local CA and issues its own.
Configure the route
Install Caddy from its official Ubuntu repository, then create a Caddy lab route in /etc/caddy/Caddyfile:
homeserver.lab.test {
tls internal
reverse_proxy 127.0.0.1:8081
}
tls internal tells Caddy to issue the certificate from its local CA instead of trying a public one (which would fail for a .test name). reverse_proxy 127.0.0.1:8081 forwards each request to the whoami container. Reload and allow HTTPS from the LAN:
sudo systemctl reload caddy
sudo ufw allow from 192.168.1.0/24 to any port 443 proto tcp
Trust the local CA on your devices
Your laptop doesn’t trust Caddy’s private CA yet, so browsers will warn. Export the root certificate from the server — Caddy stores it at /var/lib/caddy/.local/share/caddy/pki/authorities/local/root.crt — and install the local CA certificate only on devices you own. Never ask guests to install it: whoever holds a trusted root CA can impersonate any website to that device.
Verify from a client
From your laptop, request the service and inspect both proxy and container logs:
curl -I https://homeserver.lab.test
# HTTP/2 200
On the server, journalctl -u caddy -n 20 shows the TLS handshake and the proxied request, and docker compose logs web shows the request arriving on the container side. Seeing it in both places confirms the full path: client, TLS at Caddy, loopback, container.
Boundaries to keep
Protect the local root key and document client trust removal — note in your runbook which devices trust this CA and how to remove it, because that trust outlives the server.
And keep the layers straight: HTTPS does not replace application login. The proxy encrypts the path; it says nothing about who is allowed in. The most common mistake at this step is “fixing” a browser warning by clicking through it forever instead of trusting the CA properly. Warnings you train yourself to ignore stop protecting you everywhere else.
Lesson completed