thenug.net

self-hosting with pfsense, cloudflare, and nginx proxy manager - network setup explained

getting a self-hosted service onto the public internet involves more moving parts than just opening a port on your router. here's the full path a request takes from a browser to a service running on my homelab, and how pfSense, Cloudflare, and Nginx Proxy Manager work together.

the problem with self-hosting on home internet

home internet connections have two annoying properties: the public IP address changes (dynamic IP), and there's a router sitting in front of everything doing NAT. neither of these is a problem if you're just browsing the web. they're both problems if you want to self-host a website or media server from home.

the stack

browser
  └── Cloudflare (DNS + proxy)
        └── pfSense (port forward 443 → internal server)
              └── Nginx Proxy Manager (SSL termination, routing)
                    └── internal service

four layers, each doing something specific.

cloudflare

Cloudflare sits in front of the domain. all DNS for thenug.net runs through them, and the records are set to proxied — meaning traffic goes to Cloudflare's edge first, not directly to my IP.

this does a few things:

the public IP is dynamic, so a small container called cloudflare-ddns runs on the home server. it polls for the current public IP every 5 minutes and updates the DNS A records via the Cloudflare API if anything changed. no more manually updating DNS when the ISP rotates the IP.

pfsense firewall and port forwarding

pfSense runs on a Protectli Vault - a fanless mini PC with multiple Intel NICs, purpose-built for running pfSense or OPNsense as a home firewall appliance. no moving parts means it runs silent and cool 24/7. it replaced a consumer router and the difference in control and reliability is night and day.

pfSense handles the firewall and port forwarding. it has two rules that matter here:

everything else is blocked. no other ports are forwarded. the internal network isn't reachable from outside beyond these two.

nginx proxy manager

NPM runs on the internal server and is the actual entry point for all traffic. it handles two things:

SSL termination — NPM holds the Let's Encrypt certificates and decrypts HTTPS traffic. from NPM inward, everything runs over plain HTTP on the local network. the certificates auto-renew via Let's Encrypt.

routing by hostname — a single IP and port can serve multiple services because NPM inspects the Host header and routes accordingly:

thenug.net          → blog container on port 8080
jelly.thenug.net    → Jellyfin on port 8096
jellyseerr.thenug.net → Jellyseerr on port 5055

adding a new external service is just: add a DNS record in Cloudflare, add a proxy host in NPM pointing to the internal IP and port, enable SSL. done.

nat hairpinning

one quirk of this setup: accessing https://jelly.thenug.net from inside the same network usually doesn't work. the request goes out to Cloudflare, comes back to the public IP, hits pfSense — and pfSense doesn't loop it back in. this is called NAT hairpinning, and most consumer/prosumer routers handle it inconsistently.

the fix is to use internal IPs directly when on the LAN. pfSense can also be configured with NAT reflection to make external URLs work internally, but it adds complexity for not much gain.

why not just use a VPS

cloud hosting would simplify some of this - no dynamic IP, no port forwarding, no hairpinning. but running it on hardware you own means:

the whole stack costs nothing beyond hardware that was already here. a dedicated pfSense appliance like the Protectli Vault is the one investment worth making - it gives you enterprise-grade firewall features (VLANs, traffic shaping, IDS/IPS) at a one-time cost instead of a monthly subscription.

Protectli Vault FW4C fanless pfSense firewall appliance for homelab
Protectli Vault FW4C - Firewall Appliance

Fanless, 4x Intel GbE Ports - runs pfSense/OPNsense

Purpose-built for self-hosting, VLANs, and homelab networking

view on amazon

affiliate link - helps support the blog

eli5

your home doesn't have a permanent street address — it changes every so often (dynamic IP). so instead of giving people your address, you hire a post office (Cloudflare) that everyone can find. they know where to forward your mail, and whenever your address changes, a small robot (cloudflare-ddns) calls the post office and updates the forwarding record. when mail actually arrives at your door, a security guard (pfSense) checks that it came in the right way, then passes it to a receptionist (Nginx Proxy Manager) who figures out which room it belongs in based on what's written on the envelope. the rooms are things like the blog, Jellyfin, Jellyseerr. each one gets its own door, but they all come in through the same front entrance.


← back to blog