UFW Firewall Complete Guide: Rules, Rate Limiting, Logging, and Application Profiles

UFW Firewall Complete Guide: Rules, Rate Limiting, Logging, and Application Profiles

UFW (Uncomplicated Firewall) is Ubuntu’s frontend for iptables — it makes firewall management straightforward without requiring knowledge of iptables syntax. On a VPS exposed to the internet, a properly configured UFW ruleset is the first line of defense against port scanners, brute-force bots, and unauthorized service access. This guide covers everything from basic rules to advanced rate limiting and custom application profiles.

Initial Setup: Default-Deny Policy

# First: make sure you won't lock yourself out
# Always allow SSH BEFORE enabling UFW
sudo ufw allow 22/tcp

# Set default policies: deny all inbound, allow all outbound
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Enable UFW
sudo ufw enable
# "Command may disrupt existing ssh connections. Proceed with operation (y|n)?" → y

# Verify status
sudo ufw status verbose

Essential Rules for a Web Server

# Web traffic
sudo ufw allow 80/tcp    # HTTP
sudo ufw allow 443/tcp   # HTTPS

# SSH (already added above)
# Optionally restrict SSH to your IP only:
sudo ufw delete allow 22/tcp
sudo ufw allow from YOUR_HOME_IP to any port 22

# Check current rules
sudo ufw status numbered

Rule Syntax Reference

# Allow by port and protocol
sudo ufw allow 3306/tcp        # MySQL (from anywhere — dangerous, see below)
sudo ufw allow 5432/tcp        # PostgreSQL

# Allow from specific IP only (databases should NEVER be open to all)
sudo ufw allow from 10.0.0.5 to any port 5432 proto tcp    # Only from app server
sudo ufw allow from 203.0.113.0/24 to any port 22 proto tcp # Office subnet

# Deny specific IP (block an attacker)
sudo ufw deny from 198.51.100.47

# Allow a port range
sudo ufw allow 60000:61000/udp   # Mosh

# Delete a rule (by rule number from 'ufw status numbered')
sudo ufw delete 5

# Delete rule by specification
sudo ufw delete allow 3306/tcp

Rate Limiting: Block Brute Force Attacks

UFW’s limit rule blocks IPs that make more than 6 connection attempts within 30 seconds — built-in brute-force protection:

# Rate-limit SSH (replace the plain allow rule with limit)
sudo ufw delete allow 22/tcp
sudo ufw limit 22/tcp
# Blocks IPs with >6 connection attempts in 30 seconds

# Rate-limit any other port
sudo ufw limit 2222/tcp   # Custom SSH port

Service-Specific Rules

<code"># Docker services (note: Docker bypasses UFW by default — see caveat below)
# UFW rules alone do NOT protect Docker-exposed ports without extra config

# Mail server
sudo ufw allow 25/tcp    # SMTP
sudo ufw allow 587/tcp   # SMTP submission
sudo ufw allow 993/tcp   # IMAPS
sudo ufw allow 465/tcp   # SMTPS

# VPN / WireGuard
sudo ufw allow 51820/udp

# Minecraft
sudo ufw allow 25565/tcp

# Custom application ports
sudo ufw allow 8080/tcp   # Development server

Docker and UFW: The Critical Caveat

Docker modifies iptables directly and bypasses UFW rules for exposed ports (-p flag). A port exposed by Docker is reachable from the internet even if UFW says deny.

Fix: bind Docker services to 127.0.0.1 instead of 0.0.0.0:

# In docker-compose.yml — bind to localhost only:
ports:
  - "127.0.0.1:3000:3000"   # SAFE — only local
  # NOT: "3000:3000"          # UNSAFE — exposes to all interfaces, bypasses UFW

# Or fix globally by adding to /etc/docker/daemon.json:
{
  "iptables": false
}
# Then restart Docker — but this may break container networking, use with care

UFW Application Profiles

UFW supports named application profiles — predefined port sets for common services:

# List available application profiles
sudo ufw app list

# View profile details
sudo ufw app info 'Nginx Full'

# Allow by profile name (opens both port 80 and 443)
sudo ufw allow 'Nginx Full'
sudo ufw allow 'OpenSSH'

# Create a custom application profile
sudo nano /etc/ufw/applications.d/myapp
[MyApp]
title=My Application
description=Custom application on ports 8080 and 8443
ports=8080,8443/tcp
sudo ufw app update MyApp
sudo ufw allow MyApp

UFW Logging

# Enable logging
sudo ufw logging on

# Log levels: off, low, medium, high, full
sudo ufw logging medium   # Logs blocked connections

# View UFW logs
sudo grep UFW /var/log/ufw.log | tail -30
sudo tail -f /var/log/ufw.log

# Log format explained:
# [UFW BLOCK] IN=eth0 OUT= ... SRC=198.51.100.1 DST=203.0.113.47 ... DPT=3306
# DPT = destination port (what they tried to access)
# SRC = attacker's IP

Advanced: Before/After Rules via iptables

For rules that UFW’s syntax can’t express, add raw iptables rules to UFW’s before/after files:

sudo nano /etc/ufw/before.rules
# Add before the COMMIT line:

# Block ICMP ping requests (optional — some monitoring needs ping)
# -A ufw-before-input -p icmp --icmp-type echo-request -j DROP

# Rate-limit new TCP connections (SYN flood protection)
-A ufw-before-input -p tcp --syn -m conntrack --ctstate NEW \
  -m limit --limit 30/second --limit-burst 50 -j ACCEPT
-A ufw-before-input -p tcp --syn -m conntrack --ctstate NEW -j DROP
sudo ufw reload

Full Ruleset Example for a Web + App Server

sudo ufw default deny incoming
sudo ufw default allow outgoing

# SSH — rate limited, from office only
sudo ufw allow from 203.0.113.0/24 to any port 22 proto tcp
sudo ufw limit 22/tcp

# Web
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Mail (if running a mail server)
# sudo ufw allow 25/tcp
# sudo ufw allow 587/tcp
# sudo ufw allow 993/tcp

sudo ufw logging on
sudo ufw enable

sudo ufw status verbose

Getting Started

UFW is installed by default on all Ubuntu VPS instances at VPS.DO. Enable it immediately after provisioning a new server — before installing any services — so the default-deny policy is in place from the start. The emergency KVM console provides recovery access if UFW rules accidentally block your SSH connection.

Conclusion

A UFW configuration that defaults to denying all inbound traffic, allows only explicitly needed ports, rate-limits SSH, and logs blocked attempts is a significant security layer for any public VPS. The Docker bypass caveat is the most commonly missed issue — always bind Docker services to 127.0.0.1 in compose files to ensure UFW rules remain effective. Application profiles and custom before-rules extend UFW’s capabilities for complex networking requirements.

Fast • Reliable • Affordable VPS - DO It Now!

Get top VPS hosting with VPS.DO’s fast, low-cost plans. Try risk-free with our 7-day no-questions-asked refund and start today!