A practical guide to locking down any VPS (Hetzner, Hostinger, DigitalOcean, Vultr, etc.) so that admin interfaces like Coolify and SSH are only accessible through a private Tailscale network, while keeping public services (HTTP/HTTPS) open for your users.
- Ports 80/443 — open to the public (your web apps, SSL cert generation)
- SSH, Coolify dashboard, and all other ports — only accessible via Tailscale
- Admin dashboards are invisible to the internet
- No SSH brute-force attempts — port 22 is not publicly exposed
- You can manage your server securely from anywhere via Tailscale
- A VPS with Ubuntu (22.04 or 24.04)
- A Tailscale account (free for personal use)
- Tailscale installed on your local machine (laptop/desktop)
- Root/sudo access to the VPS
During initial setup, your VPS provider gives you an IP and SSH access. Use it now — we'll lock this down later.
ssh root@YOUR_SERVER_IPIf it asks "Are you sure you want to continue connecting?", type yes.
curl -fsSL https://tailscale.com/install.sh | sh
tailscale upThis will print an authentication URL. Open it in your browser, log into your Tailscale account, and authorize the device.
Once authorized, get your server's Tailscale IP:
tailscale ip -4This returns something like 100.85.99.49. Save this IP — this is how you'll access everything on your server from now on.
If you haven't already, install Tailscale on your laptop/desktop:
- macOS:
brew install tailscaleor download from tailscale.com/download - Windows: Download from tailscale.com/download
- Linux:
curl -fsSL https://tailscale.com/install.sh | sh && tailscale up
Make sure you're logged into the same Tailscale account as your server.
Verify connectivity:
ping 100.x.x.x # your server's Tailscale IPBefore locking anything down, verify everything works through Tailscale.
ssh root@100.x.x.x # Tailscale IPhttp://100.x.x.x:8000
If both work, you're ready to lock down the firewall.
Docker bypasses UFW by directly modifying iptables rules. This means ufw deny 8000 will NOT block Docker-published ports. The most reliable solution is to use your VPS provider's cloud firewall, which operates at the network level before traffic even reaches your server.
Add only these inbound rules in your provider's firewall dashboard:
| Protocol | Port | Source | Purpose |
|---|---|---|---|
| TCP | 80 | Anywhere (0.0.0.0/0) | HTTP — SSL cert generation (Let's Encrypt) + redirect to HTTPS |
| TCP | 443 | Anywhere (0.0.0.0/0) | HTTPS — secure web traffic for your users |
| ICMP | — | Anywhere (0.0.0.0/0) | Ping — useful for diagnostics |
That's it. No port 22. No port 8000. No other ports.
- SSH (port 22) is NOT open publicly. You can only SSH via Tailscale.
- Admin dashboards (port 8000, etc.) are NOT open publicly. Tailscale only.
- All other ports are blocked from the public internet.
Outbound rules: Leave as default (allow all). Don't restrict outbound or you'll break things.
Port 80 is required for Let's Encrypt SSL certificate generation and renewal. When you set up HTTPS for your domain, Let's Encrypt sends a verification request to port 80 to prove you own the domain. If blocked, your SSL certs will fail and HTTPS won't work. Port 80 also auto-redirects all traffic to HTTPS, so no unencrypted content is ever served.
If Tailscale goes down and you can't SSH in, every major VPS provider has a web console/VNC in their dashboard:
- Hetzner: Server → Rescue → Console
- Hostinger: VPS Dashboard → Browser Terminal
- DigitalOcean: Droplet → Access → Launch Droplet Console
- Vultr: Server → View Console
This gives you direct terminal access without needing SSH or Tailscale.
- Go to cloud.hetzner.com → Firewalls → Create Firewall
- Add the three inbound rules above (TCP 80, TCP 443, ICMP)
- Under Apply to, select your server
- Create the firewall
- Go to your VPS dashboard → Firewall
- Hostinger drops all incoming traffic by default
- Add Accept rules for TCP 80, TCP 443, and ICMP
- Save and activate
- Go to Networking → Firewalls → Create Firewall
- Add the three inbound rules above
- Under Apply to Droplets, select your server
- Create the firewall
- Go to Products → Network → Firewall → Add Firewall Group
- Add the three inbound rules above
- Link the firewall group to your server instance
After applying the firewall, run these tests:
# SSH via public IP — should timeout/refuse
ssh root@YOUR_PUBLIC_IP
# Admin dashboard via public IP — should timeout
http://YOUR_PUBLIC_IP:8000# SSH via Tailscale IP
ssh root@100.x.x.x
# Admin dashboard via Tailscale IP
http://100.x.x.x:8000# Web traffic
http://YOUR_PUBLIC_IP # port 80, redirects to HTTPS
https://yourdomain.com # port 443
# Ping
ping YOUR_PUBLIC_IPIf public SSH and port 8000 timeout, but Tailscale access and HTTPS work — you're locked down!
Public Internet Your Server
───────────── ──────────────
User → port 443 ──→ Cloud Firewall ──→ ✅ Allowed → Your Web App
User → port 80 ──→ Cloud Firewall ──→ ✅ Allowed → Redirect to HTTPS
User → port 22 ──→ Cloud Firewall ──→ ❌ Blocked
User → port 8000 ──→ Cloud Firewall ──→ ❌ Blocked
You (via Tailscale) Your Server
─────────────── ──────────────
You → Tailscale encrypted tunnel ──→ (bypasses firewall) ──→ ✅ All ports
ssh root@100.x.x.x SSH, Coolify,
http://100.x.x.x:8000 databases, etc.
Tailscale creates an encrypted WireGuard tunnel between your devices. Traffic through this tunnel arrives on a virtual network interface (tailscale0) directly on the server, completely bypassing the cloud firewall.
ssh root@100.x.x.x # Always use Tailscale IPhttp://100.x.x.x:8000
http://100.x.x.x:PORT
Install Tailscale on any device and log into the same account. It automatically joins the network — no config needed.
Repeat Steps 1-5 for each new VPS. All your Tailscale devices can communicate with each other automatically through the mesh network.
Since SSH is already behind Tailscale, this adds another layer:
nano /etc/ssh/sshd_configSet:
PermitRootLogin prohibit-password
PasswordAuthentication no
Restart SSH:
systemctl restart sshdadduser deploy
usermod -aG sudo deployCopy your SSH key to the new user:
mkdir -p /home/deploy/.ssh
cp ~/.ssh/authorized_keys /home/deploy/.ssh/
chown -R deploy:deploy /home/deploy/.sshThen SSH as: ssh deploy@100.x.x.x
apt update && apt upgrade -yAs Coolify warns on install, back up this file somewhere safe (not on the server):
cat /data/coolify/source/.envSave the contents to a password manager or secure notes app.
Port 22 is intentionally blocked publicly. Use your Tailscale IP:
ssh root@100.x.x.xIf Tailscale is also not working, use your VPS provider's web console/VNC as emergency access.
Make sure Tailscale is running and connected on both your laptop and the server:
tailscale statusCheck that both devices are listed and online.
Double-check the firewall is applied to the correct server in your provider's dashboard. Some providers require you to explicitly assign the firewall to a server after creating it.
Make sure port 80 is open in your cloud firewall. Let's Encrypt requires port 80 for domain verification. Also ensure your domain's DNS A record points to the server's public IP.
Verify Tailscale is connected on the server:
tailscale statusThen check Coolify is running:
docker ps | grep coolify| What | Access Method |
|---|---|
| Your web apps (HTTPS) | Public — https://yourdomain.com |
| SSL cert generation | Public — port 80 (auto, handled by Let's Encrypt) |
| SSH | Tailscale only — ssh root@100.x.x.x |
| Coolify dashboard | Tailscale only — http://100.x.x.x:8000 |
| Any other admin ports | Tailscale only — http://100.x.x.x:PORT |
| Emergency access | VPS provider's web console/VNC |
| Ping | Public — for diagnostics |