Stop Exposing Port 22: SSH to Your VPS Over Tailscale Instead

Stop Exposing Port 22: SSH to Your VPS Over Tailscale Instead

Every internet-facing VPS gets scanned within minutes of boot, and most of that traffic is bots hammering port 22. The usual fix is a pile of band-aids — fail2ban, key-only auth, a non-standard port, maybe a firewall allowlist you forget to update from your new coffee shop's IP. Tailscale SSH removes the problem instead of patching around it: close port 22 to the public internet entirely, and only reach your server over your private Tailscale network. Here's the actual setup, what it costs a solo dev (usually nothing), and the one trade-off worth thinking about before you rely on it.

Two different things are both called "Tailscale SSH"

This trips people up, so it's worth separating upfront:

  • SSH over Tailscale — you just SSH to your server's private Tailscale IP (100.x.y.z) or MagicDNS name instead of its public IP, using your normal SSH keys. Nothing changes on the server except that it's joined your tailnet. This alone lets you close port 22 publicly.
  • Tailscale SSH (the feature, enabled with tailscale up --ssh) — Tailscale's daemon takes over port 22 on the tailnet interface and authenticates connections using your Tailscale identity instead of SSH keys, with access controlled by your tailnet's policy file.

You can use either without the other. This guide sets up both: the tailnet gets you off the public internet, and Tailscale SSH removes SSH key management from the picture entirely.

If you want the two-minute version of what the feature actually does before changing anything on a production box, Tailscale's own explainer covers it directly:

Setting it up

  1. Install Tailscale on the VPS:
    curl -fsSL https://tailscale.com/install.sh | sh
  2. Bring it up with the SSH feature enabled:
    sudo tailscale up --ssh
    This authenticates the machine to your tailnet (it'll print a login URL the first time) and starts accepting Tailscale-authenticated SSH connections on the tailnet interface.
  3. Install Tailscale on your laptop the same way, and log into the same tailnet.
  4. Confirm you can reach the server by its tailnet name instead of its public IP:
    ssh username@server-name
    MagicDNS (on by default) resolves the server's tailnet hostname without you looking up its 100.x.y.z address.
  5. Once that works reliably, close port 22 to the public internet in your VPS firewall or provider's cloud firewall:
    sudo ufw delete allow 22/tcp
    sudo ufw allow in on tailscale0 to any port 22
    
    The second rule keeps SSH reachable over the Tailscale interface while the public interface no longer answers on 22 at all.

For a bit more control than "anyone on the tailnet can SSH in," add an ACL rule in your tailnet's policy file scoping SSH access to specific users and hosts, and optionally enable check mode to require re-authentication before high-risk connections (like logging in as root).

What this actually costs

Per Tailscale's current pricing page, the Personal plan is free forever for individuals: unlimited user devices, up to 6 users, up to 50 tagged resources included (additional ones are $1/month each), and basic Tailscale SSH is available on every plan, including Personal. For a solo developer running a handful of VPS instances, that's comfortably inside the free tier — you'd need more than 50 tagged servers before this costs anything.

Tailscale reworked this plan structure earlier in 2026, moving to seat-based pricing and making the free tier more generous in the process:

The catch: it's non-commercial, and it's a single point of failure

Two things worth knowing before you build this into your only way to reach production servers:

The Personal plan is explicitly non-commercial. Tailscale's own pricing FAQ states that if you create a tailnet with a public email domain (Gmail, a personal GitHub account, etc.), it's treated as personal use and auto-enrolled in the free plan — but a tailnet created with a custom domain (your business email) is treated as business use and auto-enrolled in a free trial instead, which eventually requires a paid plan. If you're a freelancer using your own domain for everything, including your Tailscale login, don't assume the free Personal tier automatically applies; check which plan your tailnet actually landed on.

Closing port 22 makes Tailscale a dependency for reaching your server at all. If your Tailscale account gets locked, the client has a bug, or you're troubleshooting from a device that isn't on the tailnet, you've removed your own way in along with everyone else's. Keep a break-glass option: most VPS providers offer a browser-based console (DigitalOcean's "Recovery Console," Hetzner's "VNC Console," etc.) that doesn't depend on SSH or Tailscale at all. Know how to reach yours before you need it, not after.

Who this is for

A strong fit if you're a solo developer or small team managing a handful of VPS instances under a personal or small-business Tailscale account, and you're currently relying on fail2ban, key rotation, or a non-standard port to manage SSH exposure. It's also useful if you SSH in from multiple devices (laptop, phone, a second machine) and want one identity model instead of distributing SSH keys to each.

Less useful if you manage infrastructure across a larger team with formal offboarding requirements — that's still doable on Tailscale, but you'll want the ACL and user-management features on a paid plan rather than Personal. And if your VPS provider's own network already restricts SSH to a fixed, trusted set of IPs you control, the incremental security gain here is smaller.

FAQ

Does this replace SSH keys entirely?
With the Tailscale SSH feature enabled, yes — authentication happens via your Tailscale identity, not a key pair. If you'd rather keep using your existing SSH keys and just stop exposing port 22 publicly, use SSH over Tailscale (skip --ssh) and connect to the tailnet address with your normal keys.

What happens if Tailscale itself goes down?
You lose the ability to reach your server over the tailnet until service is restored. This is exactly why a provider console fallback matters — test it once so you know it works before you actually need it during an outage.

Can I still allow SSH from one trusted public IP as a fallback, instead of only the provider console?
You can, but doing so reopens the exact attack surface this setup is meant to close. If you want a fallback beyond the provider console, a second break-glass credential kept offline is safer than reopening port 22 to the internet.

Does Tailscale SSH work the same way on a Raspberry Pi or home server, not just a cloud VPS?
Yes — the setup is identical. This is one of the more common home-lab use cases Tailscale explicitly supports on the free Personal plan.

Do I need a static IP or open any inbound firewall port on my router for this to work?
No. Tailscale establishes outbound connections from each device and uses NAT traversal to connect peers directly where possible, falling back to relay servers otherwise. That's the whole point — no port forwarding or static IP required.

Bottom line

For a solo dev or small team, SSH over Tailscale (with or without the Tailscale SSH feature) turns off an entire category of internet noise and brute-force attempts for the cost of installing one binary on each machine, and it's free on Tailscale's Personal plan for realistic solo-dev server counts. The trade-off isn't cost, it's a single dependency: make sure you can still get into your server if Tailscale itself is unreachable, and don't assume your account landed on the free plan just because you didn't pay anything — check it, especially if you're using a custom domain.

Sources: Tailscale SSH documentation; Tailscale pricing; Tailscale quickstart docs. Pricing and plan details verified against Tailscale's live pricing page on August 26, 2026 — plan terms change, so confirm current limits before relying on a specific number.

Comments 0

Be the first to comment.

Leave a comment