Tailscale is a private network built on WireGuard that lets your laptop, phone and servers talk to each other as if they were on one LAN, so a VPS no longer needs SSH open to the internet. On October 23, 2026, developer Pieter Levels (@levelsio) reposted advice to put VPS servers behind Tailscale for management and Cloudflare Tunnel for web traffic, "especially after" the PHP2Shell patches. This guide explains what Tailscale is, how to set the pattern up, and where it stops helping.
TL;DR
| Question | Answer |
|---|---|
| What is Tailscale? | A WireGuard-based private network (a "tailnet") between your own devices |
| What does it replace? | Public SSH. You connect to the server's private tailnet address instead |
| What about the website? | Cloudflare Tunnel makes an outbound connection to Cloudflare, so the origin opens no web ports |
| End state | Cloud firewall with no inbound rules; Tailscale and cloudflared both connect outward |
| Does it patch my software? | No. It reduces who can reach a bug, not whether the bug exists |
| Cost | Both have free plans per Levels; check current limits |
What Levels recommends
Levels describes two ways into a VPS: "via your site/app (like the PHP exploit of this week with a file upload hack)" and "via your server (like hacking into SSH or some open port)." His fix has one rule for each:
- Site and app: put them on a Cloudflare Tunnel, so the server "does not respond to port 80 or 443 anymore" and the only way in is through Cloudflare, which runs a WAF by default on the free plan.
- Server: put it on a Tailscale network, then switch off all regular access in the firewall. On Hetzner that means creating a new firewall and leaving it empty.
His reasoning for why the tunnels still work: "They don't [get in]. They go outbound." Both connect from your server out to a service, so no inbound port is needed. The thread also says to keep SSH key login anyway, and notes the setup is "much safer, not perfect."
These are one developer's recommendations, summarised by Grok on X, not a vendor standard. The mechanics below are standard Tailscale and Cloudflare documentation.
How Tailscale works
Each device you sign in joins your tailnet and gets a stable private IPv4 address in the 100.64.0.0/10 range, plus a MagicDNS name such as my-vps. Devices create encrypted WireGuard tunnels directly to each other, falling back to relays when a direct path is not possible.
A coordination server run by Tailscale handles sign-in through your identity provider (Google, GitHub, Microsoft and others), distributes public keys and applies your access rules. It does not carry your traffic in the normal direct case.
You decide who can reach what with access control policies (ACLs). The default allows everything within your tailnet, which is fine for one person and worth tightening for a team.
Guide: lock down a VPS with Tailscale
Do these in order, and keep your current SSH session open until the last step.
1. Install Tailscale on your laptop and the server
Laptop: install the app from tailscale.com/download and sign in. On the server (Linux):
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
tailscale up prints a login URL. Open it, approve the device, then confirm it joined:
tailscale status
tailscale ip -4
Read the install script before piping it to a shell if you prefer, or use your distro's package from the Tailscale install docs.
2. Connect over the tailnet
From your laptop, use the server's tailnet name or IP:
ssh youruser@my-vps
# or
ssh [email protected]
If that works, the private path is proven. Optionally enable Tailscale SSH, which lets Tailscale authenticate SSH from your identity and ACLs instead of key files:
sudo tailscale up --ssh
3. Restrict SSH to the tailnet
Open a second terminal on the tailnet connection first. Then, with UFW on Ubuntu or Debian:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow in on tailscale0 to any port 22 proto tcp
sudo ufw enable
sudo ufw status verbose
Port 22 is now reachable only on the tailscale0 interface. Test a fresh SSH connection over the tailnet, and confirm a plain ssh youruser@<public-ip> times out.
4. Close the cloud firewall
At your provider (Hetzner, DigitalOcean, AWS security groups, and similar), attach a firewall with no inbound rules. Levels says he tests this explicitly: "(test it)". Check from outside the tailnet, for example from a phone on mobile data, and scan the public IP:
nmap -Pn -p 22,80,443 <public-ip>
All three should show filtered or closed.
5. Put the website behind Cloudflare Tunnel
Add your domain to Cloudflare, then in the dashboard go to Zero Trust, Networks, Tunnels, create a tunnel and follow the install command it shows for your server. Point a public hostname at the local service:
# example ingress rule (hostname to local origin)
ingress:
- hostname: app.example.com
service: http://localhost:3000
- service: http_status:404
cloudflared connects outbound to Cloudflare. Bind your app to 127.0.0.1 so it is not reachable on the public IP even if a firewall rule is later opened by mistake. Details are in the Cloudflare Tunnel docs.
Levels also describes a variant that keeps port 443 open to Cloudflare IP ranges only, with SSH reachable via Tailscale. That works too, but the tunnel removes the open port entirely.
What this does not protect against
- Unpatched software. A tunnel is not a fix for PHP2Shell. If your PHP is older than 8.2.35, 8.3.36, 8.4.27 or 8.5.12, patch and restart every PHP process. A WAF may block some exploit traffic, but you should not rely on it.
- A compromised device or account. Anyone on your tailnet, or holding a laptop signed into it, can reach the server. Use device approval, MFA on your identity provider and tight ACLs.
- The tools themselves. On October 21, 2026, Tailscale shipped v1.104.2 for an eMudhra trust exposure; see our October 2026 attacks timeline and the eMudhra post. Keep the client and
cloudflaredupdated. See also PHP end of life after PHP2Shell if you run an old PHP branch. - Vendor dependence. Both services are companies. If that matters, the thread points to raw WireGuard, and Headscale is an open-source self-hosted coordination server. You then own its security and uptime.
- Outbound traffic. The pattern closes inbound doors. It does not stop a compromised process from calling out.
If an AI agent sets this up for you
Levels suggests asking your "clanker" to do the setup. That is reasonable, with care:
- Do not paste a long-lived Tailscale auth key into a prompt. Use a one-off, expiring, pre-approved key, ideally tagged, and revoke it afterwards.
- Review firewall commands before they run. A wrong
ufw enableis how people lock themselves out. - Keep a provider console session or rescue mode as your way back in.
Beam runs on your machine (see how to vibe code safely with Beam and Beam on a Raspberry Pi) and watches what coding agents do there, flagging risky commands and credential exposure. It does not manage your server's firewall or network, so treat this as an operations task and verify the final state yourself. For the wider principle of enforcing limits outside the agent, read Agent Baseline's secure software factory and our MCP security practical guide.
Related reading
- PHP2Shell patch guide: Ubuntu, Debian, RHEL, Docker
- PHP2Shell: how it works
- PHP2Shell and AI agents: exploit speed
- October 2026 attacks timeline
- AI agent security assessment guide
- Agent Baseline's secure software factory
Sources: Pieter Levels (@levelsio) thread, Oct 23, 2026; Tailscale docs; Cloudflare Tunnel docs; Headscale
Commands and product details are accurate as of October 23, 2026. Plans, limits and dashboards change, so check the vendor docs.

