Appearance
Remote Access
Access from outside the home network goes through Pangolin — a self-hosted, tunneled reverse proxy that fully replaces Cloudflare Tunnels. The home network never exposes a port: a connector on the LAN opens an outbound WireGuard tunnel to a VPS, and the VPS terminates HTTPS for the outside world.
Why Pangolin
- Self-hosted — same "no open home ports" model as Cloudflare Tunnels, but on own infrastructure, with no dependency on Cloudflare's terms or limits.
- Identity-aware — authentication and per-resource access control sit in front of Home Assistant; automatic SSL certificates included.
- Open source (Community Edition, AGPL-3), actively developed.
The VPS
An intentionally modest Debian virtual server at a small VPS provider:
| Resource | Value |
|---|---|
| OS | Debian |
| RAM | 4 GB — Pangolin previously ran fine with just 2 GB |
| Disk | 25 GB |
| Runs | Docker, with the Pangolin stack |
Pangolin doesn't need much; a fairly modest VM is entirely sufficient.
Firewall gotcha: Docker bypasses ufw
Docker publishes container ports by writing its own iptables NAT rules, which are evaluated before ufw's rules. A port published with -p 8080:80 is reachable from the internet even when ufw blocks it — ufw deny 8080 has no effect. In other words: Docker is fine for development, but running it on an internet-facing box without extra work silently punches holes in the firewall.
The fix used here is chaifeng/ufw-docker:
- Install the
ufw-dockerscript (e.g. to/usr/local/bin/ufw-docker) and runufw-docker install— this appends rules to/etc/ufw/after.rulesthat filter container traffic through Docker'sDOCKER-USERchain. - Restart ufw (
sudo systemctl restart ufw). - From then on, container exposure is controlled explicitly with
ufw route allow proto tcp from any to any port 443style rules; private RFC1918 ranges keep working, and public traffic to containers is denied by default.
Do this before running any container on a public VPS
Without ufw-docker (or an equivalent DOCKER-USER policy), every published port of every container is world-reachable regardless of ufw configuration.
Idea: Podman instead of Docker
Podman would sidestep the whole problem: it is daemonless, can run rootless, and doesn't rewrite the firewall the way Docker's port publishing does — so the ufw-docker workaround would become unnecessary and the attack surface smaller.
Status: to investigate — Pangolin's supported deployment is Docker Compose; whether the full stack runs cleanly under Podman today needs to be verified. Tracked in open questions.