Set Up Tailscale for Private VPS Admin Access
Lock down day-to-day server administration by reaching your VPS over Tailscale instead of exposing SSH and admin panels to the whole internet.
How to install Tailscale, join a VPS to your tailnet, test private SSH access, and reduce public exposure carefully.
Solo operators, small teams, internal dashboards, and anyone who wants simpler secure access than managing raw VPN infrastructure.
If you disable public SSH before confirming Tailscale access from another device, you can lock yourself out of the server.
Before you begin
- A VPS you can already reach with your current SSH method.
- A Tailscale account and at least one trusted laptop or workstation already signed in.
- Sudo access on the VPS.
- A safe fallback such as your VPS provider console or rescue terminal.
Tailscale gives you a private network layer on top of the public internet. Instead of leaving SSH open to every scanner on the internet, you can connect to your server using its private tailnet address. This is often easier for beginners than running a traditional VPN, and it lowers noise from bots, brute-force attempts, and accidental exposure.
Step 1: Understand what changes and what does not
Tailscale does not magically replace basic server security. You still need updates, strong authentication, and careful service configuration. What it does change is the network path. Your admin traffic can flow through a private mesh address such as 100.x.y.z or a MagicDNS hostname instead of the server's public IP.
This means you can often:
- Keep SSH reachable only over the tailnet
- Limit database or admin panel access to private interfaces
- Reduce firewall rules that exist only for your own operator access
The public web app can still stay public through Nginx or another reverse proxy. The goal is private administration, not hiding the website itself.
Step 2: Install Tailscale on the VPS
On Ubuntu or Debian, the official install script is the simplest starting point:
curl -fsSL https://tailscale.com/install.sh | shConfirm the binary is available:
tailscale versionStart the service if it is not already running:
sudo systemctl enable --now tailscaled
sudo systemctl status tailscaledNow bring the VPS into your tailnet:
sudo tailscale upThe command prints a login URL. Open it in a browser where you are already signed in to Tailscale, approve the new machine, then return to the server.
Check the assigned tailnet IP and hostname:
tailscale ip -4
tailscale statusIf you want the server to advertise itself with a memorable name, rename it from the Tailscale admin panel after enrollment.
Step 3: Test SSH over the private tailnet before changing anything else
From your laptop or workstation, list devices in Tailscale or use the MagicDNS name shown in the admin panel. Then open a new SSH session without closing your original public one:
ssh ubuntu@100.101.102.103Or, if MagicDNS is enabled:
ssh ubuntu@my-vps.tailnet-name.ts.netVerify that the new session works normally:
hostname
whoami
ip addr show tailscale0If you want easier administration, enable Tailscale SSH only after reading its behavior carefully. Many operators are fine keeping regular OpenSSH and simply binding access to the tailnet path instead.
You can also confirm that internal-only services listen on the tailnet or localhost instead of all interfaces. For example:
ss -tulpn | grep ':22\|:3000\|:8080'Step 4: Reduce public admin exposure carefully
Once the Tailscale session is proven, you can tighten access. A safe sequence is:
- Keep the website's public ports, usually
80and443, unchanged. - Restrict SSH in the firewall so only the Tailscale interface or tailnet IP range can reach it.
- Move internal dashboards to
127.0.0.1or the tailnet address where practical.
If you use UFW, first review the current rules:
sudo ufw status numberedA common pattern is allowing SSH only from the Tailscale range:
sudo ufw allow in on tailscale0 to any port 22 proto tcpThen remove the broader public SSH rule only after testing:
sudo ufw delete allow 22/tcpIf an internal web tool should stay private, bind it to localhost in its app config and reach it through an SSH tunnel or a private reverse proxy path instead of exposing another public port.
For extra control, use Tailscale ACLs in the admin console so only specific users or devices can reach production servers.
Rollback and recovery notes
The risky part of this guide is changing network access too early. If something breaks:
- Use your still-open original SSH session to revert the firewall rule.
- If you already lost SSH, use your VPS provider console to restore public access temporarily.
- Disable or loosen the new firewall rules first, then investigate whether Tailscale is connected.
Useful recovery commands:
sudo ufw status numbered
sudo ufw delete allow in on tailscale0 to any port 22 proto tcp
sudo ufw allow 22/tcp
sudo systemctl restart tailscaled
tailscale statusDo not uninstall Tailscale during initial debugging unless you are sure it is the problem. Most failures are rule-order mistakes, expired auth state, or testing from a device that is not actually on the tailnet.
Step 5: Verify the final state
By the end, you should have:
- A VPS enrolled in your tailnet
- Successful SSH access over a Tailscale IP or MagicDNS name
- Reduced or removed public exposure for SSH and other admin-only services
- A fallback recovery path documented before making future changes
This is one of the highest-leverage small security upgrades you can make on a self-hosted server.
Troubleshooting common Tailscale setup problems
The server joined Tailscale, but I cannot SSH over the tailnet.
Confirm the device is online in tailscale status, verify you are connecting from another logged-in tailnet device, and check firewall rules for tailscale0.
The login URL expired during setup.
Run sudo tailscale up again and approve the machine from the fresh URL.
MagicDNS name does not resolve.
Use the raw Tailscale IP first. Then verify MagicDNS is enabled in the Tailscale admin settings.
I removed public SSH and now only some devices can connect.
Review your Tailscale ACLs, confirm the devices are approved in the tailnet, and make sure they are not stuck offline.
Admin panel is still publicly reachable.
Check whether the app is bound to 0.0.0.0 or published with Docker port mappings like 0.0.0.0:3000:3000. Tailscale helps, but application listen addresses still matter.
What to do next
Once private access is working, the next strong move is improving visibility into what your containers are doing. Continue with Centralize Docker Logs With Loki and Grafana.
