What
A layered remote access setup that lets me reach any VM in the homelab from anywhere — without exposing any ports on the home router. Three mechanisms, each for a different use case:
- WireGuard VPN — full network access to all VLANs (for admin work)
- SSH Bastion — jump host for shell sessions (Proxmox, routers)
- Cloudflare Tunnel — HTTPS-only exposure for web dashboards (Grafana, Proxmox UI)
Why
Port-forwarding is the lazy option but it’s a liability — every exposed port is an attack surface. At Datanet we use out-of-band management networks for exactly this reason. I replicated the same philosophy at home: management traffic never mixes with production traffic, and nothing is directly internet-exposed.
How
WireGuard on a VPS
A cheap VPS (Hetzner CAX11, €4/mo) acts as the WireGuard endpoint. It has a public IP; the homelab doesn’t need one.
# /etc/wireguard/wg0.conf (server)
[Interface]
PrivateKey = <server_private_key>
Address = 10.200.0.1/24
ListenPort = 51820
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer] # homelab gateway
PublicKey = <homelab_public_key>
AllowedIPs = 10.200.0.2/32, 10.10.0.0/16
The pfSense VM has a WireGuard peer pointing to the VPS. A static route on pfSense pushes 10.200.0.0/24 traffic out the tunnel.
SSH Bastion (Proxmox host)
Proxmox is accessible only via the WireGuard tunnel on VLAN 99. SSH is hardened:
# /etc/ssh/sshd_config (relevant lines)
PermitRootLogin no
PasswordAuthentication no
AllowUsers montadher
MaxAuthTries 3
Client ~/.ssh/config:
Host bastion
HostName 10.10.99.10
User montadher
IdentityFile ~/.ssh/id_ed25519
ProxyJump none # already inside VPN
Host lab-*
User montadher
ProxyJump bastion
Cloudflare Tunnel for Dashboards
For services I want to access from a browser without the full VPN:
cloudflared tunnel create homelab
cloudflared tunnel route dns homelab grafana.nerdcore.pro
Grafana and the Proxmox web UI are exposed via cloudflared running as a systemd service inside the lab. Cloudflare Zero Trust policies restrict access to my email address only.
Challenges
- Split DNS — when on the VPN,
grafana.nerdcore.proshould resolve to the internal IP, not the Cloudflare tunnel. Added a pfSense Unbound override:grafana.nerdcore.pro → 10.10.20.50. - WireGuard roaming — my laptop changes IP (mobile data → WiFi). WireGuard handles this natively since it’s connectionless; no reconnect needed.
- VPS firewall — initially left the VPS with default Hetzner firewall (port 22 + 80 + 443 open). Locked it down to only WireGuard (UDP 51820) and SSH from a fixed IP.
Tech Used
- WireGuard — VPN tunnel
- Hetzner VPS — public endpoint (CAX11)
- cloudflared — Cloudflare Tunnel daemon
- Cloudflare Zero Trust — identity-aware access control
- OpenSSH — bastion / jump host
- pfSense — VPN client + routing