Skip to content

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:

ResourceValue
OSDebian
RAM4 GB — Pangolin previously ran fine with just 2 GB
Disk25 GB
RunsDocker, 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:

  1. Install the ufw-docker script (e.g. to /usr/local/bin/ufw-docker) and run ufw-docker install — this appends rules to /etc/ufw/after.rules that filter container traffic through Docker's DOCKER-USER chain.
  2. Restart ufw (sudo systemctl restart ufw).
  3. From then on, container exposure is controlled explicitly with ufw route allow proto tcp from any to any port 443 style 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.