The first time I opened a port for a web app on a cloud server, I edited iptables rules directly. I was following a tutorial, copy-pasting commands I didn't really understand. It worked, but it felt fragile — like one typo away from locking myself out.
UFW (Uncomplicated Firewall) is what I use now. It wraps iptables in an interface that actually makes sense, without hiding the important concepts. Once I spent an hour really learning UFW, I stopped dreading firewall configuration entirely.
This guide covers everything from basic allow/deny rules to application profiles, rate limiting, and logging — with real examples from web server and app deployment scenarios.
I use this on Tencent Cloud Lighthouse alongside the Lighthouse console firewall. The two operate independently at different layers — UFW controls OS-level packet filtering, while Lighthouse's console-level firewall blocks traffic before it even reaches your server's network stack. This independent layering is genuinely useful: if you misconfigure UFW and drop SSH access, the console firewall keeps the server protected while you use OrcaTerm (Lighthouse's browser terminal) to restore the UFW rules without needing a local SSH connection. The combination gives you defense in depth without additional complexity.
Key Takeaways
- Always
sudo ufw allow sshbeforesudo ufw enableto avoid lockout- Lighthouse console firewall + UFW = two independent protection layers
- Use
ufw allow from IP to any port PORTfor database and internal service rulessudo ufw status verboseshows all current rules and default policiessudo ufw delete NUMBERremoves a specific rule (get numbers withufw status numbered)
UFW manages two types of rules:
The default policy matters:
default deny incoming = block everything unless explicitly alloweddefault allow outgoing = let your server make outbound connections freelyThis is the correct setup for most servers: restrictive inbound, permissive outbound.
UFW is typically pre-installed on Ubuntu but may not be active:
# Install if needed
sudo apt install -y ufw
# Check current status
sudo ufw status
Before enabling, always add an SSH rule:
# Allow SSH — critical before enabling
sudo ufw allow ssh
# Equivalent to: sudo ufw allow 22/tcp
Set default policies:
sudo ufw default deny incoming
sudo ufw default allow outgoing
Enable the firewall:
sudo ufw enable
# Type "y" when prompted
Check status:
sudo ufw status verbose
sudo ufw allow ssh # Port 22 TCP
sudo ufw allow http # Port 80 TCP
sudo ufw allow https # Port 443 TCP
sudo ufw allow 3000 # TCP and UDP
sudo ufw allow 3000/tcp # TCP only
sudo ufw allow 51820/udp # UDP only (WireGuard)
sudo ufw allow 6000:6100/tcp
# Allow all traffic from a trusted IP
sudo ufw allow from 192.168.1.100
# Allow SSH from a specific IP only
sudo ufw allow from 192.168.1.100 to any port 22
sudo ufw allow from 10.0.0.0/24
sudo ufw deny 8080
sudo ufw deny from 203.0.113.0
UFW includes named profiles for common applications. These are more readable than raw port numbers:
# List available application profiles
sudo ufw app list
Common profiles you'll see:
Available applications:
Nginx Full # Ports 80 and 443
Nginx HTTP # Port 80 only
Nginx HTTPS # Port 443 only
OpenSSH # Port 22
Apache # Port 80
Apache Full # Ports 80 and 443
Apache Secure # Port 443 only
Use them instead of port numbers:
sudo ufw allow 'Nginx Full'
sudo ufw allow 'OpenSSH'
# Allow HTTP only on eth0 (not on a VPN interface)
sudo ufw allow in on eth0 to any port 80
# Allow connections to PostgreSQL from localhost only
sudo ufw allow from 127.0.0.1 to any port 5432
sudo ufw deny out to 203.0.113.0
UFW can rate-limit connections to reduce the impact of brute-force attacks. It blocks IPs that attempt more than 6 connections in 30 seconds:
# Rate limit SSH
sudo ufw limit ssh
# Rate limit a custom port
sudo ufw limit 2222/tcp
This is a lighter-weight alternative to Fail2ban for SSH — though Fail2ban provides more sophisticated analysis and longer bans.
Enable logging to see what UFW is blocking:
sudo ufw logging on
# Logging levels: off, low, medium, high, full
sudo ufw logging medium
View the logs:
sudo tail -f /var/log/ufw.log
# Show only blocked connections
sudo grep "BLOCK" /var/log/ufw.log | tail -20
# Most active blocked IPs
sudo grep "BLOCK" /var/log/ufw.log | awk '{print $13}' | sort | uniq -c | sort -rn | head -10
sudo ufw status numbered
Output example:
Status: active
To Action From
-- ------ ----
[ 1] 22/tcp ALLOW IN Anywhere
[ 2] 80/tcp ALLOW IN Anywhere
[ 3] 443/tcp ALLOW IN Anywhere
[ 4] 3000/tcp ALLOW IN Anywhere
# Delete rule number 4
sudo ufw delete 4
sudo ufw delete allow 3000/tcp
sudo ufw delete allow 'Nginx Full'
sudo ufw disable # Turns off all rules
sudo ufw enable # Re-enables with existing rules
sudo ufw reset
# Removes all rules and disables UFW — use with caution
UFW rules are evaluated in order — the first matching rule wins.
This means a blanket allow rule earlier in the list can override a specific deny later. For example:
sudo ufw allow from 10.0.0.0/24 # Rule 1: allow entire subnet
sudo ufw deny from 10.0.0.50 # Rule 2: won't work — Rule 1 matches first
To block a specific IP within an allowed subnet, the deny rule must come first:
sudo ufw insert 1 deny from 10.0.0.50 # Insert at position 1 (before existing rules)
sudo ufw allow from 10.0.0.0/24 # Allow the rest of the subnet
Use sudo ufw status numbered to see rule order and sudo ufw insert N to add rules at a specific position.
sudo ufw allow ssh
sudo ufw allow 'Nginx Full' # or 'Apache Full'
sudo ufw enable
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
# Don't open port 3000 — Nginx proxies to it internally
sudo ufw enable
sudo ufw allow ssh
sudo ufw allow 51820/udp
sudo ufw enable
sudo ufw allow ssh
sudo ufw allow 25565/tcp
sudo ufw enable
sudo ufw allow ssh
# Don't open 3306 (MySQL) or 5432 (PostgreSQL) — access from localhost only
sudo ufw enable
| Issue | Likely Cause | Fix |
|---|---|---|
| Connection refused | Service not running or wrong port | Check systemctl status SERVICE and verify firewall rules |
| Permission denied | Wrong file ownership or permissions | Check file ownership with ls -la and use chown/chmod to fix |
| 502 Bad Gateway | Backend service not running | Restart the backend service; check logs with journalctl -u SERVICE |
| SSL certificate error | Certificate expired or domain mismatch | Run sudo certbot renew and verify domain DNS points to server IP |
| Service not starting | Config error or missing dependency | Check logs with journalctl -u SERVICE -n 50 for specific error |
| Out of disk space | Logs or data accumulation | Run df -h to identify usage; clean logs or attach CBS storage |
| High memory usage | Too many processes or memory leak | Check with htop; consider upgrading instance plan if consistently high |
| Firewall blocking traffic | Port not open in UFW or Lighthouse console | Open port in Lighthouse console firewall AND sudo ufw allow PORT |
What does UFW stand for?
Uncomplicated Firewall. It's a frontend for iptables (the Linux kernel's built-in firewall) that provides a simpler command syntax.
Will enabling UFW lock me out of SSH?
Only if you don't allow port 22 first. Always run sudo ufw allow ssh before sudo ufw enable. On Lighthouse, the console-level firewall provides a safety net if you misconfigure UFW.
What's the difference between ufw allow and ufw allow from?
ufw allow 80 opens port 80 from any IP. ufw allow from 192.168.1.0/24 to any port 3306 allows MySQL only from a specific IP range. Use the latter for sensitive services like databases.
How do I check UFW status and current rules?
Run sudo ufw status verbose to see all rules and the default policy. sudo ufw status numbered shows rules with numbers for easy deletion.
Build on a solid foundation:
👉 Tencent Cloud Lighthouse — Cloud servers with console-level firewall protection
👉 View current pricing and promotions
👉 Explore all active deals and offers