setting up a local domain
I've been messing around with my local network lately, trying to add services and little tools to make things run smoothly like I posted about previously. One thing that had been annoying me is that the URLs I was using for local services were either getting proxied over the wider internet or they were unwieldy generic names with a port number tacked on.
It was time to set up a locally routable domain.
I've created a mkcert self-signed wildcard certificate for *.mydomain.internal and created a couple of DNS entries on my Headscale VPN control server. These point to a VM I'm using for various internal network things and get handled by a Caddy reverse proxy. The mkcert wildcard is good for two years so, hopefully I'll have something better before then.
How it fits together
The setup is pretty straightforward once you understand the moving parts. When a device on my network tries to reach something like dashboard.mydomain.internal, the request hits my VPN control server's DNS first. It resolves the wildcard entry and points traffic at the internal VM, where Caddy picks it up and routes it to whichever service is actually running on whatever port.
The wildcard certificate
The wildcard certificate is the part that makes this feel clean. Rather than generating a new cert for every subdomain, mkcert let me create one cert that covers everything under *.mydomain.internal. Since mkcert installs its own local CA, any device that trusts that CA will get a valid green padlock — no browser warnings, no certificate exceptions to click through.
The catch is that you need to distribute the CA certificate to every device you want to use. For my machines that was easy enough; phones and tablets are a bit more of a faff depending on the OS. Potentially I could just use a "real" domain and go with Let's Encrypt to make this even more smooth, but I wanted to be sure I could still run the whole setup even without any internet connectivity at all.
Caddy config
On the Caddy side, configuration stays simple too. Adding a new internal service is just a matter of dropping a new reverse proxy block into the Caddyfile pointing at the right host and port, and it automatically picks up the wildcard cert. No reloading nginx, no fiddling with certificate paths.
The two-year problem
The two-year expiry on the cert is the one thing that gives me mild anxiety. My plan is to look at getting proper internal PKI sorted before then — something like Step CA would let me run short-lived certs and automate renewal properly. But for now, a calendar reminder and a manually regenerated cert is good enough.
It's one of those things that feels like a lot of setup upfront but pays off immediately. Local services actually feel like real services now rather than 192.168.1.47:8123 scribbled in a notes app somewhere.