
Homelab 2: one HTTPS entrance for all your services
Every service you host needs an address and a padlock. Rather than opening one door per service, open one: a reverse proxy. It receives every visitor, holds the HTTPS certificates, and passes each request to the right service inside your network. Ours is Nginx Proxy Manager (NPM): open source, with a web interface, and it renews Let's Encrypt certificates by itself.
A bit of history
Igor Sysoev released nginx in 2004 to serve many simultaneous connections with little memory; it became one of the most used web servers in the world. Let's Encrypt, opened to the public at the end of 2015, made HTTPS certificates free and automatic. Nginx Proxy Manager wraps both in a web interface: one form per site, and a certificate that renews itself.
Proof it is simple
This site went online exactly this way: a container serving the pages, one proxy entry, one certificate, about fifteen minutes. A small VM is plenty for a whole homelab.
1. The name
At your domain registrar, create an A record for your domain and one for each subdomain (or a wildcard *), pointing to your public IP address. Check from outside your network that the name resolves before going further.
2. The box
In your internet box, forward ports 80 and 443 to the machine that runs the proxy. Nothing else.
Never forward port 81: it is NPM's administration page. It stays reachable from your home network only.
3. The proxy
On a VM or container with Docker (see Homelab 1), the official compose file, version pinned:
services:
npm:
image: jc21/nginx-proxy-manager:2.16.0
restart: unless-stopped
ports:
- "80:80"
- "81:81"
- "443:443"
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
docker compose up -d
Open http://<proxy-address>:81 from your network. A default administrator account is created on the first start (see the setup page of the official documentation): log in, then change its email and password right away.
4. Publish a service
- Hosts → Proxy Hosts → Add Proxy Host.
- Domain names:
yourdomain.tld,www.yourdomain.tld. - Forward to the service's address and port on your network, for example
192.0.2.10and8000. - Tab SSL: request a new Let's Encrypt certificate, tick Force SSL.
Save: Let's Encrypt checks that you own the name through port 80, issues the certificate, and NPM renews it before it expires.
Traps
Pin a version instead of :latest: otherwise an update arrives the day you restart, unannounced. And never add privileged: true out of habit: a proxy does not need full control of the host.
If certificate requests keep failing, stop retrying: Let's Encrypt limits how many identical certificates you can get per week. Fix the cause first, usually port 80 not forwarded or a name that does not resolve yet.
Testing from home and the site does not answer? Some boxes cannot loop back to their own public address. Test from a phone on mobile data before suspecting the proxy.
Sources
Found this useful? Tell us what to measure next.