The previous two posts covered two separate pieces: routing public traffic through a Cloudflare Tunnel, and reaching a server for administration through Tailscale. This post connects them, end to end, starting from an empty Hetzner account and finishing with a VM that has no open inbound ports at all — not even one.
1. Provision the VM in Hetzner
-
Create a new project in the Hetzner Cloud Console
-
Add a server: pick a nearby location, a minimal Debian or Ubuntu image, and the smallest instance type that fits the workload — this kind of setup doesn’t need much horsepower
-
Add your SSH public key during creation so root password login is never even enabled
-
Skip enabling any extra public services at this stage; we’ll lock the network down before anything else touches it
2. Attach a Deny-by-Default Hetzner Firewall
Before installing anything else, attach a Hetzner Cloud Firewall to the server with no inbound rules at all. All outbound traffic stays allowed, since both Cloudflare Tunnel and Tailscale only ever initiate outbound connections — the VM reaches out to them, never the other way around. That single firewall, with an empty inbound rule set, is what makes the rest of this setup safe: even if a service on the VM were misconfigured later, there’s no network path in for it to be reached through.
3. Set Up the Cloudflare Tunnel
Follow the steps from the Cloudflare Tunnel post: install cloudflared, authenticate it, create the tunnel, and point its ingress rule at whatever is serving the site locally (port 80 on the VM). Since the firewall has no inbound rules, this is the only way public traffic reaches the server, and it does so without a single port being opened for it.
4. Join the Tailnet for SSH
Follow the steps from the Tailscale post: install Tailscale on the VM, authenticate it into the same tailnet as your laptop, and enable Tailscale SSH. Because the Hetzner firewall already blocks all inbound traffic, port 22 was never reachable from the public internet in the first place — Tailscale just gives you, specifically, a private path back in.
5. The Resulting Architecture
| Layer | Handles | Reachable by |
|---|---|---|
| Hetzner Cloud Firewall | Default-deny inbound safety net | Nobody — zero inbound rules |
| Cloudflare Tunnel | Public website traffic | Anyone, via Cloudflare’s edge |
| Tailscale | SSH / administrative access | Only devices enrolled in the tailnet |
Three layers, each doing exactly one job, and none of them requiring a single open inbound port on the VM itself. The firewall stays closed by default because it never has to be opened — both the traffic visitors send and the traffic you send as an admin arrive through connections the VM initiated outward, not ones the internet initiated inward.