Secure Your VPS with Tailscale: Only Ports 80 and 443 Open to the World
Secure Your VPS with Tailscale: Only Ports 80 and 443 Open to the World
February 19, 2026 (6 days ago)
Written by Temps Team
Last updated February 19, 2026 (6 days ago)
You spin up a VPS, install your deployment platform, and push your first app. It works. You feel good.
Then you run netstat -tlnp and realize your server is listening on six ports. Port 22 for SSH. Port 80 and 443 for web traffic. Port 8080 for your admin API. Port 5432 for PostgreSQL. Maybe a few more you forgot about.
Every one of those ports is a door. And every door is being knocked on — constantly. Bots scan the entire IPv4 address space in under 45 minutes. Your brand-new server gets its first brute-force attempt within hours of going live.
Most developers know they should "secure their server." Few actually do it properly. This guide shows you how to lock down your VPS using Tailscale so that the only ports exposed to the public internet are the ones that need to be: 80 and 443.
The Problem: Every Open Port Is an Attack Surface
What's Actually Exposed on a Typical VPS
Here's what a freshly provisioned VPS running a deployment platform typically exposes:
| Port | Service | Needs Public Access? |
|---|---|---|
| 22 | SSH | No |
| 80 | HTTP (Let's Encrypt + redirects) | Yes |
| 443 | HTTPS (production traffic) | Yes |
| 5432 | PostgreSQL | No |
| 8080 | Admin API / Dashboard | No |
| 6379 | Redis | No |
| 2375 | Docker API | No |
Only two of those ports serve your users. The rest are management and infrastructure services that should never be reachable from the public internet.
What Happens When You Leave Them Open
This isn't theoretical. Here's what actually happens to exposed services:
- SSH (port 22) — Automated bots try thousands of username/password combinations per hour. Even with key-only auth, you're burning CPU on failed handshakes and filling logs with noise.
- PostgreSQL (port 5432) — If exposed, attackers attempt default credentials (
postgres/postgres). A single successful connection means full database access — all your users' data, all your secrets. - Admin APIs (port 8080) — Your deployment platform's API often has endpoints for creating deployments, reading environment variables, and managing infrastructure. One leaked API key and an attacker controls your entire platform.
- Docker API (port 2375) — An exposed Docker socket gives an attacker root-equivalent access to your server. They can pull your images, read your environment variables, and spin up crypto miners.
- Redis (port 6379) — Redis has no authentication by default. An exposed Redis instance can be overwritten to inject SSH keys, giving attackers direct shell access.
This isn't about sophisticated zero-day exploits. These are automated scans that hit every IP address on the internet, every day. Your server isn't special — it's just another target in the queue.
Firewalls Help, But They're Not Enough
UFW or iptables rules are a good start:
ufw default deny incoming
ufw default allow outgoing
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow 22/tcp
ufw --force enable
This blocks the database and admin ports from the internet. Good. But you still have problems:
- SSH is still public — you need it to manage the server, so it stays open. Every IP on earth can attempt to connect.
- No remote database access — your firewall blocks port 5432, but now you can't connect from your local machine to debug a production issue.
- No remote admin access — the Temps dashboard runs on port 8080, but it's behind the firewall. You can only access it through SSH tunnels, which are clunky and break constantly.
- IP allowlisting is fragile — you can allowlist your home IP, but it changes. Your co-founder works from a coffee shop. Your CI server has a dynamic IP. You spend more time updating firewall rules than writing code.
You end up in a frustrating middle ground: either your services are too exposed, or you can't reach the tools you need.
The Solution: Tailscale as Your Private Network Layer
Tailscale creates an encrypted peer-to-peer mesh network using WireGuard. Every device you add gets a stable private IP (in the 100.x.x.x range) that works regardless of where you are — home, office, hotel, or phone.
The key insight: services that bind to your Tailscale IP are only reachable by devices on your Tailscale network. The public internet can't see them. No ports to scan, no doors to knock on.
How It Fits Your Deployment Stack
Public Internet Tailscale Network (private)
───────────── ──────────────────────────
┌────────────────────┐
Users ──── :443 ────────>│ │<──── :8080 ── You (laptop)
Users ──── :80 ────────>│ Your VPS │<──── :5432 ── You (local DB tool)
│ │<──── :22 ── You (SSH)
Bots ──── :22 ────X │ Tailscale IP: │
Bots ──── :5432 ───X │ 100.x.x.x │<──── Your co-founder
Bots ──── :8080 ───X └────────────────────┘<──── Your CI server
Public ports (80, 443): Serve your web traffic. These are the only ports your firewall allows from the internet.
Private ports (everything else): Bound to the Tailscale interface. Only reachable by authenticated devices on your network. Invisible to the rest of the world.
Step-by-Step: Locking Down Your Temps VPS with Tailscale
Prerequisites
- A VPS running Temps (any cloud provider — Hetzner, DigitalOcean, AWS, etc.)
- A Tailscale account (free for personal use, up to 100 devices)
- Tailscale installed on your local machine
Step 1: Install Tailscale on Your VPS
SSH into your server and install Tailscale:
curl -fsSL https://tailscale.com/install.sh | sh
tailscale up
Follow the authentication link to add the server to your Tailscale network. Once connected, your server gets a stable IP like 100.64.0.1.
Verify the connection:
tailscale ip -4
# 100.64.0.1
Step 2: Configure the Firewall — Only 80 and 443
Now lock down the firewall to only allow web traffic from the public internet:
# Reset existing rules
ufw --force reset
# Default policies
ufw default deny incoming
ufw default allow outgoing
# Only allow HTTP and HTTPS from the public internet
ufw allow 80/tcp
ufw allow 443/tcp
# Allow all traffic from the Tailscale interface
ufw allow in on tailscale0
# Enable the firewall
ufw --force enable
The critical line is ufw allow in on tailscale0. This permits all traffic over the Tailscale encrypted tunnel — SSH, database connections, admin access — without opening any ports to the public internet.
Step 3: Verify SSH Works Over Tailscale
Before you close your current SSH session, open a new terminal and test:
# Connect using the Tailscale IP
ssh root@100.64.0.1
If this works, SSH over the public internet is blocked but SSH over Tailscale works. You're in.
Do not close your existing SSH session until you've verified this. If something goes wrong, you still have your original connection to fix it.
Step 4: Bind Admin Services to Tailscale Only
Configure Temps to bind its admin console to the Tailscale IP instead of 0.0.0.0:
# Get your Tailscale IP
TAILSCALE_IP=$(tailscale ip -4)
# Update Temps to bind the console to the Tailscale interface only
# Public traffic (80/443) stays on 0.0.0.0
temps serve \
--address="0.0.0.0:80" \
--tls-address="0.0.0.0:443" \
--console-address="${TAILSCALE_IP}:8080"
Now the Temps dashboard is only accessible from your Tailscale network. No SSH tunnel needed — just open http://100.64.0.1:8080 in your browser.
Step 5: Secure the Database
PostgreSQL should already be bound to 127.0.0.1. To access it remotely over Tailscale, update the bind address to include the Tailscale IP:
# In your PostgreSQL config or Docker compose
# Before: 127.0.0.1:5432:5432
# After: bind to both localhost and Tailscale
POSTGRES_HOST=127.0.0.1,${TAILSCALE_IP}
Now you can connect from your laptop using any database client:
psql -h 100.64.0.1 -U temps -d temps
No SSH tunnels. No port forwarding. No exposed ports. Just a direct, encrypted connection over your private network.
What Your Server Looks Like After
Before (Typical VPS)
$ nmap your-server-ip
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
443/tcp open https
5432/tcp open postgresql
8080/tcp open http-alt
Five open ports. Three of them shouldn't be public. Every port scanner on the internet can see them all.
After (Tailscale-Secured VPS)
$ nmap your-server-ip
PORT STATE SERVICE
80/tcp open http
443/tcp open https
Two ports. Both required. Everything else is invisible. Your server's attack surface just shrank by 60%.
An attacker scanning your IP sees a web server and nothing else. No SSH to brute-force. No database to probe. No admin panel to exploit.
Tailscale Features That Matter for VPS Security
MagicDNS
Instead of remembering 100.64.0.1, Tailscale gives your server a DNS name:
ssh root@my-vps.tailnet-name.ts.net
psql -h my-vps.tailnet-name.ts.net -U temps -d temps
ACLs (Access Control Lists)
Control which team members can access which services:
{
"acls": [
{
"action": "accept",
"src": ["group:devs"],
"dst": ["tag:servers:80,443,8080"]
},
{
"action": "accept",
"src": ["group:admins"],
"dst": ["tag:servers:*"]
}
]
}
Developers get web and dashboard access. Only admins get SSH and database access. Enforced at the network level, not by application-layer authentication that can be bypassed.
SSH Console (Tailscale SSH)
Tailscale can replace OpenSSH entirely, eliminating SSH keys, authorized_keys files, and port 22:
# On the server
tailscale up --ssh
# On your laptop (no SSH keys needed)
tailscale ssh root@my-vps
Authentication happens through your Tailscale identity provider. No keys to rotate. No .ssh/config to manage. No port 22 to open — not even on the Tailscale interface.
Funnel (Optional Public Access)
Need to temporarily expose a development service? Tailscale Funnel lets you create a public URL without touching firewall rules:
tailscale funnel 3000
# https://my-vps.tailnet-name.ts.net:443/ -> http://localhost:3000
When you're done, turn it off. No firewall rules to remember to revert.
Combining Tailscale with Temps Security Features
Tailscale handles network-level access. Temps handles application-level security. Together they provide defense in depth.
Temps Firewall + IP Access Control
Even with Tailscale, configure Temps to restrict which IPs can access your admin API:
# Only allow Tailscale IPs to access the admin console
bunx @temps-sdk/cli firewall allow 100.64.0.0/10 --description "Tailscale network"
Temps Security Headers
Temps automatically injects security headers on all responses:
| Header | Value |
|---|---|
| Content-Security-Policy | Strict policy limiting resource origins |
| X-Frame-Options | DENY |
| X-Content-Type-Options | nosniff |
| Strict-Transport-Security | max-age=31536000; includeSubDomains |
| Referrer-Policy | strict-origin-when-cross-origin |
| Permissions-Policy | Restrictive defaults |
Temps Security Scanner
Run a security scan to verify your lockdown:
bunx @temps-sdk/cli security scan
This checks for exposed database ports, weak TLS configurations, missing security headers, and other common issues. After setting up Tailscale, your scan score should improve significantly — no more warnings about exposed management ports.
Encrypted Secrets
Environment variables in Temps are encrypted with AES-256-GCM. Combined with Tailscale's encrypted transport, your secrets are encrypted at rest and in transit on a network nobody else can reach.
The Full Security Checklist
After following this guide, your VPS should meet every item:
- Only ports 80 and 443 open to the public internet
- SSH accessible only via Tailscale (not port 22 on public IP)
- Database bound to localhost and Tailscale IP (not
0.0.0.0) - Admin dashboard accessible only via Tailscale
- Tailscale ACLs configured for team access control
- UFW enabled with deny-by-default policy
- Temps security headers enabled (default)
- Temps security scan passing with no critical findings
- SSL/TLS certificates auto-renewed via Let's Encrypt
- Environment variables encrypted at rest
What This Costs
| Component | Cost |
|---|---|
| Tailscale (personal) | Free (up to 100 devices) |
| Tailscale (team) | $6/user/month |
| Temps | Free (self-hosted) |
| VPS (Hetzner CX22) | ~$6/month |
| Total | $6-12/month |
Compare this to the cost of a security breach: leaked customer data, compliance violations, reputation damage, incident response, and notification requirements. A $6/month VPN is the cheapest security investment you'll ever make.
Getting Started
Install Temps on your VPS, lock it down with Tailscale, and deploy your application — all in under 15 minutes:
# Install Temps
curl -fsSL https://temps.sh/deploy.sh | bash
# Install Tailscale
curl -fsSL https://tailscale.com/install.sh | sh
tailscale up
# Lock down the firewall
ufw --force reset
ufw default deny incoming
ufw default allow outgoing
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow in on tailscale0
ufw --force enable
# Deploy your app
bunx @temps-sdk/cli login
bunx @temps-sdk/cli deploy my-app -e production -y
Two ports open. Everything else behind an encrypted mesh network. Your users get fast, reliable HTTPS. Your infrastructure stays invisible.
For more on Temps security features, check our security documentation. For Tailscale setup details, see the Tailscale quickstart.