For years my homelab ran on self-signed certificates and a browser full of permanent exceptions. It works, technically, but it trains you to click through warnings, which is precisely the habit you don't want. The thing I'd assumed was impossible turned out to be easy: real, trusted certificates for services that are never reachable from the internet.
The trick is the DNS-01 challenge. Most people meet Let's Encrypt through HTTP validation, where it hits port 80 on your box to prove you own the name. That obviously can't work for nas.internal.example.com sitting on a private VLAN. DNS-01 instead asks you to publish a TXT record, so the box never needs to be reachable at all. It just needs to update DNS.
I run Caddy as the reverse proxy in front of everything, with the DNS plugin for my provider. Caddy handles the ACME dance itself, drops a TXT record in via the API, gets the certificate, and renews it before it expires without me thinking about it. A vhost is three lines:
nas.internal.example.com {
reverse_proxy 192.168.20.10:5000
tls { dns cloudflare {env.CF_API_TOKEN} }
}
That's the whole thing. Internal name, public certificate, no exposed ports, green padlock. The only fiddly bit is scoping the DNS API token so it can edit one zone and nothing else, which you absolutely should do. I no longer click through a single warning, which means when a real one appears I'll actually notice it.