UFW: The Complete Linux Firewall Guide — From Zero to Hardened Server
1. What is UFW and Why Should You Care
UFW — Uncomplicated Firewall — is a frontend for iptables, the kernel-level packet filtering system built into Linux. If iptables is a powerful but arcane language, UFW is a readable translator: you write human-friendly commands, and UFW writes the iptables rules behind the scenes.
But "uncomplicated" doesn't mean "limited". UFW is the default firewall tool on Ubuntu and is used in production by millions of servers worldwide. It handles IPv4 and IPv6 simultaneously, supports application profiles, interface-specific rules, IP whitelisting, rate limiting, and rich logging — all through a consistent, memorable command syntax.
Who is UFW for?
- System administrators managing VPS, dedicated servers, or Raspberry Pis
- Developers who need to quickly secure a Linux machine without a deep iptables background
- DevOps engineers setting up CI/CD nodes, container hosts, or self-hosted services
- Homelab enthusiasts running services like Nextcloud, Home Assistant, Plex, or Nginx on local hardware
UFW vs. the alternatives
- iptables: maximum power, steep learning curve, easy to shoot yourself in the foot
- nftables: modern replacement for iptables, powerful, but even more complex to manage manually
- firewalld: zone-based, default on Fedora/RHEL/CentOS — do NOT run alongside UFW
- UFW: best balance of simplicity and capability for Ubuntu/Debian-based systems
⚠️ Never run UFW and firewalld simultaneously. They both manage iptables and will conflict unpredictably. Pick one.
2. How UFW Works Under the Hood
Understanding UFW's internals saves you hours of debugging. When you run any ufw command, three things happen:
- UFW updates its rule files in /etc/ufw/
- It translates those rules into iptables (and ip6tables) commands
- The kernel's netfilter subsystem enforces the rules on every packet
The rule files you should know
/etc/ufw/ufw.conf— master switch: whether UFW is enabled at boot, logging level/etc/ufw/before.rules— rules applied before UFW's user rules (useful for NAT, custom chains)/etc/ufw/after.rules— rules applied after user rules (cleanup, reject fallback)/etc/ufw/before6.rules/after6.rules— same, for IPv6/etc/ufw/user.rules— your actual ufw allow/deny rules, managed automatically/etc/default/ufw— default policies and IPv6 toggle/etc/ufw/applications.d/— application profile definitions (one file per app)
Packet flow through UFW chains
When a packet arrives, the kernel processes it through these chains in order:
ufw-before-input→ allows loopback, established connections, ICMPufw-user-input→ your explicit allow/deny rulesufw-after-input→ handles NetBIOS, DHCP, and finally REJECT for unmatched traffic
This layered approach means UFW is fail-safe by design: unmatched traffic hits the default policy (usually DENY) at the end.
Verify UFW is managing your iptables
sudo iptables -L | grep ufw
You'll see chains like ufw-before-input, ufw-user-input, ufw-user-limit confirming UFW owns the rule set.
3. Installation and Prerequisites
UFW comes pre-installed on Ubuntu 18.04+. On fresh Debian or Raspberry Pi OS installs, you may need to add it:
sudo apt update && sudo apt install ufw -y
Check the current status before touching anything
sudo ufw status verbose
On a fresh server this returns Status: inactive. Good — that means no rules are accidentally blocking you yet.
The golden rule: allow SSH before you enable UFW
🔒 **If you are connected to this machine via SSH and you enable UFW without allowing SSH first, you will be locked out immediately.** There is no grace period. Run the allow rule first, every single time.
# Always do this BEFORE sudo ufw enable
sudo ufw allow ssh
# or equivalently:
sudo ufw allow 22/tcp
The safe startup sequence
# 1. Allow SSH (critical)
sudo ufw allow ssh
# 2. Set sane defaults
sudo ufw default deny incoming
sudo ufw default allow outgoing
# 3. Add your service rules (web, etc.)
sudo ufw allow http
sudo ufw allow https
# 4. Enable — only after step 1-3
sudo ufw enable
# 5. Confirm
sudo ufw status verbose
4. Default Policies — The Foundation of Everything
Default policies are the rules that apply to all traffic that doesn't match any of your explicit rules. Getting these right is more important than any individual rule you'll add.
The recommended defaults for a server
sudo ufw default deny incoming # block all inbound by default
sudo ufw default allow outgoing # allow all outbound by default
sudo ufw default deny routed # block forwarding (unless you're a router)
Why deny incoming and allow outgoing?
- Deny incoming: your server should only accept connections you explicitly allow. Every open port that you didn't consciously open is a potential attack surface.
- Allow outgoing: your server needs to fetch updates, talk to external APIs, send emails, query DNS. Blocking outgoing breaks most services silently and is rarely worth the complexity for standard servers.
- Deny routed: unless your Linux machine acts as a router or VPN gateway, you don't want it forwarding packets between interfaces.
When you might want to restrict outgoing too
High-security environments (payment processing, medical data, government) sometimes also restrict outgoing traffic — but this requires careful planning. You'd need to explicitly allow: DNS (port 53), NTP (port 123), apt package sources, and every external service your application talks to.
# Restrictive outgoing (advanced — plan carefully)
sudo ufw default deny outgoing
sudo ufw allow out 53 # DNS
sudo ufw allow out 123 # NTP
sudo ufw allow out 80 # HTTP (apt, etc.)
sudo ufw allow out 443 # HTTPS
sudo ufw allow out 587 # SMTP submission (email)
💡 For most servers, deny incoming + allow outgoing is the right choice. Locking down outgoing is a security enhancement for high-compliance environments, not a general recommendation.
5. Allowing Services — Every Syntax You Need
Allow by port number
sudo ufw allow 22 # SSH (TCP+UDP)
sudo ufw allow 80 # HTTP
sudo ufw allow 443 # HTTPS
sudo ufw allow 3306 # MySQL
sudo ufw allow 5432 # PostgreSQL
sudo ufw allow 6379 # Redis
sudo ufw allow 27017 # MongoDB
Allow by port + protocol (more precise)
sudo ufw allow 22/tcp # SSH — TCP only (correct, SSH doesn't use UDP)
sudo ufw allow 53/tcp # DNS over TCP
sudo ufw allow 53/udp # DNS over UDP
sudo ufw allow 1194/udp # OpenVPN
sudo ufw allow 51820/udp # WireGuard
Allow by service name (reads /etc/services)
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw allow ftp
sudo ufw allow smtp
sudo ufw allow imaps
# See all available service names:
cat /etc/services | grep -v "^#" | head -50
Allow a port range
# Allow ports 8000 through 8100 (TCP)
sudo ufw allow 8000:8100/tcp
# Allow ports 60000-61000 (UDP) — common for Mosh
sudo ufw allow 60000:61000/udp
Using application profiles (the cleanest way)
UFW application profiles are named bundles of port rules. Software packages install profiles in /etc/ufw/applications.d/ automatically.
# List all available profiles
sudo ufw app list
# See details for a specific profile
sudo ufw app info 'Nginx Full'
# Allow a profile
sudo ufw allow 'Nginx Full' # ports 80 + 443
sudo ufw allow 'Nginx HTTP' # port 80 only
sudo ufw allow 'Nginx HTTPS' # port 443 only
sudo ufw allow 'Apache Full'
sudo ufw allow 'OpenSSH'
Creating a custom application profile
You can create your own profiles for apps that don't ship one:
# Create /etc/ufw/applications.d/myapp
sudo tee /etc/ufw/applications.d/myapp > /dev/null << 'EOF'
[MyApp]
title=My Custom Application
description=Runs on port 8080 TCP
ports=8080/tcp
EOF
# Reload and use it
sudo ufw app update MyApp
sudo ufw allow MyApp
6. Blocking Traffic — IPs, Subnets, Ports
Block a specific IP address
# Block all traffic from an IP
sudo ufw deny from 203.0.113.100
# Block a specific IP from reaching a specific port
sudo ufw deny from 203.0.113.100 to any port 22
Block a subnet (CIDR notation)
# Block an entire /24 subnet
sudo ufw deny from 203.0.113.0/24
# Block a /16 (larger range)
sudo ufw deny from 192.168.0.0/16
# Block a known malicious ASN range (check bgp.tools for ranges)
sudo ufw deny from 1.2.0.0/16
Block a specific port for everyone
# Block incoming MySQL from external (allow only localhost)
sudo ufw deny 3306
# Block Telnet (you should never have this open anyway)
sudo ufw deny 23/tcp
The DENY vs REJECT difference
- DENY (DROP): silently drops the packet. The sender gets no response and has to wait for timeout. Slower for attackers to scan, but also slower for legitimate users to get feedback.
- REJECT: sends back a "connection refused" response immediately. Faster feedback, but confirms the port exists to scanners.
# Reject instead of deny (sends RST/ICMP unreachable back)
sudo ufw reject from 203.0.113.100
sudo ufw reject 3306
For most external traffic, deny (drop) is preferred — it slows down scanners and doesn't reveal information.
7. IP-Specific and Source-Restricted Rules
Allow a specific IP to access everything
# Whitelist your office IP or home IP
sudo ufw allow from 203.0.113.50
Allow a specific IP to access only specific ports
# Allow IP 203.0.113.50 to SSH only
sudo ufw allow from 203.0.113.50 to any port 22 proto tcp
# Allow a subnet to access MySQL
sudo ufw allow from 10.0.0.0/8 to any port 3306
# Allow only your monitoring server to access metrics port
sudo ufw allow from 10.10.1.100 to any port 9100 proto tcp
Practical example: database server locked down
A common production pattern: your database server should NEVER be reachable from the internet. Only your application servers should reach it:
# On the database server:
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Only allow SSH from management network
sudo ufw allow from 10.0.1.0/24 to any port 22 proto tcp
# Only allow MySQL from app server IPs
sudo ufw allow from 10.0.2.10 to any port 3306 proto tcp
sudo ufw allow from 10.0.2.11 to any port 3306 proto tcp
sudo ufw allow from 10.0.2.12 to any port 3306 proto tcp
sudo ufw enable
Allowing a subnet for internal services
# Allow your entire LAN to reach any port
sudo ufw allow from 192.168.1.0/24
# Allow VPN subnet to reach internal services
sudo ufw allow from 10.8.0.0/24 to any port 8080 proto tcp
8. Interface-Specific Rules
When your server has multiple network interfaces — say, eth0 for public internet and eth1 for a private internal network — you can apply rules per interface. This is especially useful for VPS with both public and private IPs, multi-homed servers, and containers.
Check your interfaces
ip -br addr
Allow/block on a specific interface
# Allow HTTP only on the public interface
sudo ufw allow in on eth0 to any port 80
# Allow MySQL only on private interface (never expose DB to public)
sudo ufw allow in on eth1 to any port 3306
# Block all traffic coming in on eth0 from a specific IP
sudo ufw deny in on eth0 from 203.0.113.100
# Allow SSH only from private network interface
sudo ufw allow in on eth1 to any port 22 proto tcp
Loopback — always leave it alone
UFW automatically allows loopback (127.0.0.1 / ::1) traffic. Never block the loopback interface — it would break virtually every service that communicates internally.
# This is handled automatically by UFW in /etc/ufw/before.rules:
# -A ufw-before-input -i lo -j ACCEPT
# -A ufw-before-output -o lo -j ACCEPT
# Verify it's there:
sudo cat /etc/ufw/before.rules | grep " lo "
9. Rate Limiting — UFW's Built-In Brute-Force Protection
UFW has a built-in rate limiting feature that blocks IPs making too many connection attempts. It uses iptables' recent module to track connection frequency.
The default threshold: if an IP attempts to connect more than 6 times in 30 seconds, UFW blocks it temporarily.
Enable rate limiting on SSH (highly recommended)
# Use 'limit' instead of 'allow' for SSH
sudo ufw limit ssh
# or equivalently:
sudo ufw limit 22/tcp
🛡️ sudo ufw limit ssh is one of the single most impactful commands you can run on a public-facing Linux server. It stops the overwhelming majority of automated SSH brute-force attacks.
Rate limiting on other services
# Rate limit login endpoints or APIs
sudo ufw limit 8080/tcp
# Rate limit a custom port
sudo ufw limit 2222/tcp
How it works internally
UFW's rate limiting translates to two iptables rules: one that tracks recent connections using the --recent module, and one that blocks IPs exceeding the threshold. You can inspect these with:
sudo iptables -L ufw-user-limit -n -v
Combine with Fail2ban for persistent blocking
UFW's rate limiting is stateless — a banned IP can reconnect after the 30-second window. For persistent banning of repeat offenders, combine UFW with Fail2ban:
sudo apt install fail2ban -y
# fail2ban reads auth.log and calls ufw to ban IPs
# /etc/fail2ban/jail.local:
# [sshd]
# enabled = true
# banaction = ufw
# maxretry = 3
# bantime = 3600
10. Viewing, Deleting, and Managing Rules
View current rules
# Simple status
sudo ufw status
# Verbose — shows default policies, logging, interface rules
sudo ufw status verbose
# Numbered — required for deletion by ID
sudo ufw status numbered
Delete rules
# Method 1: Delete by rule specification
sudo ufw delete allow 80
sudo ufw delete allow from 203.0.113.101
sudo ufw delete allow 'Nginx Full'
# Method 2: Delete by rule number (get numbers from 'status numbered')
sudo ufw status numbered
sudo ufw delete 3 # deletes rule #3
💡 When you delete a rule by specification, UFW deletes both the IPv4 and IPv6 variants. When you delete by number, you delete only that specific entry — make sure you're deleting the right one.
Reset all rules (nuclear option)
# Wipe everything and disable UFW
sudo ufw reset
# UFW will ask for confirmation and back up existing rules to:
# /etc/ufw/*.rules.YYYYMMDD_HHMMSS
Reload rules without disabling
# Apply rule changes without a full restart
sudo ufw reload
Backup and restore your ruleset
# Backup
sudo cp -r /etc/ufw /etc/ufw.backup.$(date +%Y%m%d)
# Restore
sudo cp /etc/ufw.backup.20260211/* /etc/ufw/
sudo ufw reload
11. Logging — Understanding What UFW is Blocking
UFW logs are essential for debugging access issues and understanding attack patterns. By default, UFW logs at 'low' level — but you can tune this.
Logging levels
sudo ufw logging off # disable logging entirely
sudo ufw logging low # logs blocked packets + invalid packets (default)
sudo ufw logging medium # also logs allowed packets that match rules
sudo ufw logging high # all of the above + rate-limited packets
sudo ufw logging full # everything, including allowed packets (very verbose)
Where are UFW logs?
# UFW logs go to syslog and kernel log, then rsyslog routes them:
sudo tail -f /var/log/ufw.log
# Or in journald on modern systems:
sudo journalctl -f | grep UFW
# Or in syslog:
sudo grep UFW /var/log/syslog | tail -30
Reading a UFW log line
# Example log entry:
Feb 11 03:15:22 hostname kernel: [UFW BLOCK] IN=eth0 OUT=
MAC=... SRC=203.0.113.5 DST=192.168.1.10 LEN=44 TOS=0x00
PREC=0x00 TTL=238 ID=54321 PROTO=TCP SPT=45678 DPT=3306
WINDOW=1024 RES=0x00 SYN URGP=0
[UFW BLOCK]— the packet was blockedIN=eth0— arrived on the eth0 interfaceSRC=203.0.113.5— source IP (this is who's trying to connect)DST=192.168.1.10— destination IP (your server)DPT=3306— destination port (MySQL — someone is scanning for exposed databases)PROTO=TCP SYN— it's a new TCP connection attempt
Log analysis one-liners
# Top 10 blocked source IPs
sudo grep "UFW BLOCK" /var/log/ufw.log | grep -oP 'SRC=\K[^ ]+' | sort | uniq -c | sort -rn | head 10
# Most targeted destination ports
sudo grep "UFW BLOCK" /var/log/ufw.log | grep -oP 'DPT=\K[^ ]+' | sort | uniq -c | sort -rn | head 10
# Count blocks per hour
sudo grep "UFW BLOCK" /var/log/ufw.log | awk '{print $1, $2, substr($3,1,2)}' | uniq -c
12. The Docker + UFW Problem (and How to Fix It)
This is the most common gotcha with UFW in production. Docker directly manipulates iptables to expose container ports — and it does so in a way that completely bypasses UFW rules. This means: if you run docker run -p 8080:80 myapp, port 8080 is exposed to the ENTIRE internet, even if you have no UFW rule allowing it.
🚨 **This is not a bug — it's by design. But it means UFW gives you a false sense of security if you're running Docker.** Running sudo ufw status may show port 8080 is not allowed, yet it's fully accessible from the internet.
Solution 1: Bind containers to localhost (simplest)
# Bind the container port to localhost only
docker run -p 127.0.0.1:8080:80 myapp
# Then use Nginx as a reverse proxy (UFW rules apply to Nginx)
# UFW: allow 80, allow 443 (for Nginx)
# Nginx proxies to 127.0.0.1:8080 internally
Solution 2: Disable Docker's iptables management
# /etc/docker/daemon.json
{
"iptables": false
}
# Then: sudo systemctl restart docker
# Warning: this breaks container networking features
# You'll need to manually manage routing
Solution 3: Use DOCKER-USER chain (recommended for advanced setups)
# Docker creates a DOCKER-USER chain that IS processed before DOCKER chain
# Rules here are UFW-compatible
sudo iptables -I DOCKER-USER -i eth0 -j DROP
sudo iptables -I DOCKER-USER -i eth0 -s 203.0.113.50 -j RETURN
# Make it persistent (with iptables-persistent):
sudo apt install iptables-persistent
sudo netfilter-persistent save
13. IPv6 with UFW
UFW handles IPv4 and IPv6 simultaneously by default. Every rule you create generates both an IPv4 and an IPv6 version (you'll see (v6) entries in sudo ufw status).
Verify IPv6 is enabled
grep IPV6 /etc/default/ufw
# Should show: IPV6=yes
IPv6-specific rules
# Allow SSH from a specific IPv6 address
sudo ufw allow from 2001:db8::1 to any port 22 proto tcp
# Block an IPv6 subnet
sudo ufw deny from 2001:db8::/32
# Allow a service on IPv6 only (rarely needed — usually rules apply to both)
sudo ufw allow in on eth0 to 2001:db8::1 port 443
If you want to disable IPv6 rules
# In /etc/default/ufw, change:
IPV6=no
# Then reload:
sudo ufw reload
14. Advanced: NAT and Port Forwarding with UFW
If your Linux machine acts as a gateway — a VPN server, a home router, or a bastion host — you may need NAT rules. These can't be done with standard ufw commands; they require editing /etc/ufw/before.rules directly.
Enable IP forwarding (required for NAT)
# /etc/ufw/sysctl.conf (or /etc/sysctl.conf)
net/ipv4/ip_forward=1
net/ipv6/conf/default/forwarding=1
# Apply:
sudo sysctl -p
NAT masquerading example (share internet via UFW)
# Add to TOP of /etc/ufw/before.rules (before *filter line):
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 10.8.0.0/8 -o eth0 -j MASQUERADE
COMMIT
# Then update /etc/default/ufw:
DEFAULT_FORWARD_POLICY="ACCEPT"
# And reload:
sudo ufw reload
15. Ready-to-Use Configurations for Common Server Types
Web Server (Nginx or Apache + SSH)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw limit ssh # rate-limited SSH
sudo ufw allow http # port 80
sudo ufw allow https # port 443
sudo ufw enable
Web + Mail Server
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw limit 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 25/tcp # SMTP
sudo ufw allow 465/tcp # SMTPS
sudo ufw allow 587/tcp # Submission
sudo ufw allow 993/tcp # IMAPS
sudo ufw allow 995/tcp # POP3S
sudo ufw enable
Database Server (private network only)
sudo ufw default deny incoming
sudo ufw default allow outgoing
# SSH only from management subnet
sudo ufw allow from 10.0.1.0/24 to any port 22 proto tcp
# DB access only from app servers
sudo ufw allow from 10.0.2.0/24 to any port 5432 proto tcp # PostgreSQL
# No public access at all
sudo ufw enable
WireGuard VPN Server
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp # SSH for management
sudo ufw allow 51820/udp # WireGuard
# After enabling: ensure IP forwarding is on and NAT rules in before.rules
sudo ufw enable
Raspberry Pi / Homelab Server
sudo ufw default deny incoming
sudo ufw default allow outgoing
# SSH — local network only
sudo ufw allow from 192.168.1.0/24 to any port 22 proto tcp
# Home Assistant
sudo ufw allow from 192.168.1.0/24 to any port 8123 proto tcp
# Plex
sudo ufw allow 32400/tcp
# Samba (if using file sharing on LAN only)
sudo ufw allow from 192.168.1.0/24 to any port 445 proto tcp
sudo ufw enable
Minecraft / Game Server
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw limit 22/tcp
sudo ufw allow 25565/tcp # Minecraft Java
sudo ufw allow 25565/udp # Minecraft Bedrock
sudo ufw allow 19132/udp # Bedrock alternate
sudo ufw enable
16. Troubleshooting UFW — Diagnosing Access Problems
"I can't connect to my service after enabling UFW"
# Step 1: Check if the rule exists
sudo ufw status verbose | grep 8080
# Step 2: Check if the service is actually listening
ss -tlnp | grep 8080
# Step 3: Check UFW logs for BLOCK entries
sudo grep "UFW BLOCK" /var/log/ufw.log | grep DPT=8080
# Step 4: Temporarily disable UFW to confirm it's the firewall
sudo ufw disable
# (test your connection)
sudo ufw enable
"I got locked out via SSH"
If you're on a VPS, use the provider's out-of-band console (DigitalOcean Recovery Console, AWS EC2 Serial Console, etc.) to access the machine without SSH, then:
sudo ufw allow ssh
sudo ufw reload
# or if you need to start over:
sudo ufw disable
"UFW is active but traffic isn't being filtered"
This often happens with Docker (see Section 12) or if another tool like firewalld is also running:
# Check for firewalld conflict
sudo systemctl status firewalld
# Check raw iptables to see who's winning
sudo iptables -L -n -v | head -50
# Verify UFW chains are in place
sudo iptables -L | grep ufw
"My rule isn't working as expected — rule ordering matters"
UFW processes rules in order. A DENY rule added before an ALLOW rule will take precedence. View the order with:
sudo ufw status numbered
# Rules are evaluated top-to-bottom
# Use 'ufw insert' to add a rule at a specific position:
sudo ufw insert 1 allow from 10.0.0.1 to any port 22
"Port is open to internet despite UFW block (Docker issue)"
# Test from outside your network (not from localhost):
nmap -p 8080 YOUR_SERVER_IP
# If it's open despite UFW blocking it, Docker is bypassing UFW
# Solution: bind container to localhost (see Section 12)
docker run -p 127.0.0.1:8080:80 myapp
17. Production Hardening Checklist
✅ Run through this checklist every time you set up a new server or audit an existing one.
✅ Must-do for every server
- [ ] Default deny incoming policy set
- [ ] SSH allowed before UFW was enabled
- [ ] SSH rate-limited with
ufw limit ssh(not justufw allow ssh) - [ ] Only ports that are actually needed are open
- [ ] UFW enabled and set to start at boot
- [ ] Logging enabled (at minimum 'low' level)
- [ ] Rules verified with
sudo ufw status verbose - [ ] Actual connectivity tested from outside the server
✅ For public-facing servers
- [ ] SSH restricted to specific IPs if possible (not open to world)
- [ ] Only ports 80 and 443 open for web traffic
- [ ] No database ports (3306, 5432, 27017, 6379) open to public internet
- [ ] Fail2ban installed and configured alongside UFW
- [ ] UFW logs reviewed weekly or piped to a log aggregator
✅ For servers running Docker
- [ ] All containers bound to 127.0.0.1 (not 0.0.0.0) for internal ports
- [ ] Reverse proxy (Nginx) handles all public traffic, not containers directly
- [ ] Understood that Docker bypasses UFW for ports exposed on 0.0.0.0
✅ For high-security environments
- [ ] Outgoing traffic also restricted to known-good destinations
- [ ] SSH access via VPN or bastion host only — port 22 not open to public internet
- [ ] UFW rules backed up and version-controlled
- [ ] Regular audit:
sudo ufw status numbered— remove rules that are no longer needed
18. UFW Quick Reference Card
# ── Status ──────────────────────────────────────
sudo ufw status
sudo ufw status verbose
sudo ufw status numbered
# ── Enable / Disable ─────────────────────────────
sudo ufw enable
sudo ufw disable
sudo ufw reload
sudo ufw reset # wipe all rules
# ── Default Policies ─────────────────────────────
sudo ufw default deny incoming
sudo ufw default allow outgoing
# ── Allow ────────────────────────────────────────
sudo ufw allow 22/tcp # by port+proto
sudo ufw allow ssh # by service name
sudo ufw allow 'Nginx Full' # by app profile
sudo ufw allow 8000:8100/tcp # port range
sudo ufw allow from 10.0.0.1 # specific IP
sudo ufw allow from 10.0.0.0/24 to any port 5432 # IP + port
# ── Limit (rate limiting) ────────────────────────
sudo ufw limit ssh
# ── Deny / Reject ────────────────────────────────
sudo ufw deny 3306
sudo ufw deny from 203.0.113.100
sudo ufw reject 23/tcp
# ── Interface-specific ───────────────────────────
sudo ufw allow in on eth0 to any port 80
sudo ufw deny in on eth0 from 203.0.113.5
# ── Delete Rules ─────────────────────────────────
sudo ufw delete allow 80
sudo ufw delete 3 # delete by rule number
# ── Logging ──────────────────────────────────────
sudo ufw logging low
sudo ufw logging medium
sudo tail -f /var/log/ufw.log