Step 4
Step 4 — SSH tunnels + loopback binding
25 min
Step 4 — SSH tunnels + loopback binding
The fewer ports your server exposes, the better. 127.0.0.1 binding + SSH tunnels is the standard answer.
The problem — 0.0.0.0 binding
postgres:
ports:
- "5432:5432" # exposed to the entire internet
Bots will hammer port 5432 for credentials 24/7.
The fix — loopback binding
postgres:
ports:
- "127.0.0.1:5432:5432" # only the server itself
External cannot reach it.
Reach it through SSH tunnel
ssh -L 5433:127.0.0.1:5432 user@my-server.com
Your laptop's localhost:5433 → SSH tunnel → server's 127.0.0.1:5432. Bots can't follow without your SSH key.
psql -h localhost -p 5433 -U user -d myapp
autossh for persistence
autossh -f -N -L 5433:127.0.0.1:5432 user@my-server.com
-f background, -N no shell.
Which ports stay open
| Port | Public? |
|---|---|
| 22 (SSH, key-only) | ✅ |
| 80, 443 (Caddy) | ✅ |
| 5432 (PostgreSQL) | ❌ — loopback |
| 6379 (Redis) | ❌ — loopback |
| 3000+ (apps) | ❌ — Caddy proxies |
Only three ports public.
ufw firewall
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw default deny incoming
sudo ufw enable
Try it
Spin up an EC2 free instance, apply the rules above, and nmap your-ip. Only three ports should appear.
Going deeper
Next
Step 5 — AWS essentials.