I have a dozen little services running at home, and until last week every single one of them threw a certificate warning. Self-signed certs everywhere, that yellow browser scaresheet I had trained myself to click through, and the nagging worry that one day I would click through a warning that actually meant something.
So I fixed it properly. One nginx box now sits in front of everything as a reverse proxy, terminating TLS with real Let's Encrypt certificates, and the warnings are gone. No more exceptions, no more imported root CAs on every device in the house.
The trick for internal hosts is the DNS challenge. The usual HTTP challenge wants Let's Encrypt to reach your box on port 80 from the public internet, which is no good when the service is sat on a private RFC1918 address with no inbound path. The DNS-01 challenge sidesteps that entirely. You prove control of the domain by dropping a TXT record, so nothing internal needs to be exposed. My DNS is hosted somewhere with an API, so the renewal is fully automatic.
The nginx side is dull, which is exactly what you want from infrastructure:
server {
listen 443 ssl;
server_name grafana.home.example.com;
ssl_certificate /etc/letsencrypt/live/home.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/home.example.com/privkey.pem;
location / {
proxy_pass http://10.0.10.20:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
I issued one wildcard-ish setup by getting a cert covering home.example.com and a SAN list of the handful of subdomains I actually use, then renew it on a cron with the deploy hook reloading nginx. Wildcards over ACME are not a thing yet, so a SAN list it is. When I add a service I edit the domain list, re-run, reload, done.
The bit I almost forgot: HSTS. Once everything has a valid cert it is tempting to switch on strict transport security straight away. Resist that urge until you are certain every internal name resolves and serves over HTTPS, because HSTS is sticky and a browser that has cached it will refuse to fall back. I learned that the gentle way, on a test domain, rather than the painful way on the one I actually use.
One more thing worth doing while you are in there: get the renewal monitored. A cert that auto-renews is wonderful right up until the API token expires, the DNS plugin breaks on an upgrade, or the cron silently stops firing, and the first you hear of it is a browser warning ninety days later. I added a tiny check that alerts me if any cert has under a fortnight left on it. Cheap insurance against the failure mode where automation quietly stops automating and nobody notices.
It is a small change and it took an evening. But walking up to any device on my network, typing a hostname, and getting a clean green padlock without a single warning is one of those quietly satisfying things that makes the homelab feel less like a pile of hacks and more like a system.