Configure a Firewall with UFW on Ubuntu

Khimananda Oli 8 min read Database
Configure a Firewall with UFW on Ubuntu

By Khimananda Oli | Last reviewed: August 2026

Leaving an Ubuntu server exposed to the public internet without a host-based firewall is a critical security risk that invites automated scanning and brute-force attacks. To properly configure a firewall with UFW on Ubuntu, you must define explicit allow rules for essential services like SSH and HTTP before enabling the default deny policy. This guide provides the exact commands and safety checks I use in production environments to ensure connectivity is maintained while hardening the attack surface. For a broader hardening strategy beyond just the firewall, refer to my guide on initial Ubuntu server setup to secure a fresh VPS.

Internet TrafficUFW FirewallDefault: DENY INALLOW TCP 22ALLOW TCP 443DROP TCP 23Ubuntu Services(SSH, Nginx)Configure a Firewall with UFW on Ubuntu: Default Deny + Explicit Allow
UFW acts as a gatekeeper, enforcing a default-deny policy while permitting only explicitly allowed ports like SSH and HTTPS.

How do you safely configure a firewall with UFW on Ubuntu without locking yourself out?

The most common mistake engineers make when they first configure a firewall with UFW on Ubuntu is enabling the service before defining an allow rule for their current SSH session. UFW operates on a "default deny" basis for incoming traffic once active; if port 22 (or your custom SSH port) is not whitelisted, you will be permanently locked out of the server and forced to use a cloud provider's rescue console or physical access to recover.

Step 1: Verify SSH Access and Install UFW

Before touching any firewall rules, confirm your current SSH connection is stable and identify which port you are using. On modern Ubuntu LTS releases (22.04, 24.04, and 26.04), UFW is pre-installed. If it is missing, install it via APT:

sudo apt update
sudo apt install ufw -y
sudo systemctl status ufw

Step 2: Set Default Policies Before Enabling

Establish the baseline security posture. These commands do not activate the firewall but prepare the rule set. We allow all outbound traffic because servers typically need to fetch updates, call external APIs, or connect to databases. Inbound traffic is denied by default until explicitly permitted.

sudo ufw default deny incoming
sudo ufw default allow outgoing

Step 3: Whitelist SSH Immediately

This is the non-negotiable safety step. If you use the standard port 22, run sudo ufw allow ssh. If you have changed SSH to a non-standard port (e.g., 2222) as part of your AWS EC2 security hardening, you must specify it numerically:

# Standard SSH
sudo ufw allow ssh

# Custom SSH port example
sudo ufw allow 2222/tcp

# Limit SSH to specific IP range (recommended for production)
sudo ufw allow from 203.0.113.0/24 to any port 22 proto tcp

Step 4: Enable UFW and Confirm

Only after verifying your SSH rule should you enable the firewall. The --force flag skips the interactive prompt, which is useful for automation scripts but requires absolute certainty that SSH is allowed.

sudo ufw enable
sudo ufw status verbose

What are the essential UFW commands for managing web applications and databases?

Once the baseline SSH access is secured, configuring application-specific rules follows the same explicit pattern. When you deploy web stacks like those described in my Laravel on Ubuntu VPS deployment guide, you need precise control over HTTP, HTTPS, and database ports.

  • Web Servers: Use application profiles when available. sudo ufw allow 'Nginx Full' opens both port 80 and 443. For Apache, use 'Apache Full'. This is cleaner than managing individual port rules.
  • Custom Application Ports: For Node.js, Python, or Go apps running directly on the host, specify the exact port and protocol: sudo ufw allow 3000/tcp. Avoid opening UDP unless your application specifically requires it.
  • Database Access: Never expose database ports (3306, 5432, 27017) to any. Always restrict to specific application server IPs or private subnets: sudo ufw allow from 10.0.1.5 to any port 5432.
  • Rate Limiting: Protect against brute force on sensitive ports. sudo ufw limit ssh denies connections from IPs attempting 6+ connections within 30 seconds. This complements fail2ban but operates at the netfilter level.
UFW Rule Evaluation Order (First Match Wins)Incoming PacketMatch Rule #1? (e.g., LIMIT SSH)NoMatch Rule #2? (e.g., ALLOW 443)NoMatch Rule #N? (e.g., ALLOW 8080)NoDEFAULT DENYYes → ACTIONYes → ACTION
UFW evaluates rules sequentially from top to bottom; the first matching rule determines the packet fate, making rule order critical.

How does UFW compare to iptables and nftables for Ubuntu firewall management?

Understanding where UFW fits in the Linux networking stack prevents misconfiguration. UFW is not a firewall itself; it is a frontend that generates rules for the underlying kernel packet filter. In Ubuntu 26.04, the backend is nftables, though UFW abstracts this complexity entirely. Here is how they compare for real-world operations:

CriteriaUFWiptables (legacy)nftables (native)
ComplexityLow — human-readable syntaxHigh — chain/table verbosityMedium — structured but detailed
PersistenceAutomatic on enable/disableRequires iptables-persistentNative config file support
Audit TrailClean ufw status outputDense rule lists, hard to reviewReadable sets/maps, moderate
Best ForSingle-host, app servers, VPSLegacy systems, complex NATRouters, high-perf filtering
SOC 2 EvidenceEasy to export & documentDifficult to maintain evidenceGood with proper documentation

For 95% of Ubuntu server workloads—including Laravel apps, Node.js APIs, and container hosts—UFW provides the right balance of security and operability. Reserve direct nftables manipulation for edge routers or when you need advanced features like concatenated maps that UFW cannot express. If you are orchestrating infrastructure across multiple servers, consider managing these rules via Infrastructure as Code with Terraform or Ansible to ensure consistency.

How do you troubleshoot UFW rules and avoid common configuration mistakes?

Even experienced engineers encounter issues when they configure a firewall with UFW on Ubuntu. Most problems stem from rule ordering, protocol mismatches, or misunderstanding how UFW interacts with Docker and Kubernetes.

Diagnosing Blocked Connections

Enable logging to see exactly what UFW is dropping. Set the log level to medium or high temporarily during troubleshooting:

sudo ufw logging medium
sudo tail -f /var/log/ufw.log | grep BLOCKED

Look for the DPT= field to identify the destination port being blocked. If you see legitimate traffic being dropped, check whether the rule specifies the correct protocol (TCP vs UDP) and whether the source IP matches your actual client address (cloud load balancers often use different source IPs than expected).

Docker and UFW Conflict Resolution

This is the single most frequent issue in modern deployments. Docker manipulates iptables/nftables directly and bypasses UFW rules by default. A container bound to 0.0.0.0:8080 will be accessible regardless of UFW denying port 8080. To fix this, edit /etc/docker/daemon.json:

{
  "iptables": false
}

Then restart Docker and manage container port exposure exclusively through UFW or a reverse proxy. Alternatively, bind containers to 127.0.0.1 and let Nginx/Caddy handle external routing—a pattern I strongly recommend for production security and one covered in my Docker for beginners guide.

Rule Ordering and Deletion

Because UFW uses first-match logic, overly broad rules placed early can shadow more specific restrictions later. Always review numbered rules before making changes:

sudo ufw status numbered
sudo ufw delete [NUMBER]

When adding restrictive rules (like IP whitelists), insert them at the top using sudo ufw prepend allow from 10.0.0.0/8 to any port 3306 rather than appending, ensuring they take precedence over broader allowances.

❌ Insecure Config✓ Hardened UFW Configdefault allow incomingallow 22 from anyallow 3306 from anyallow 8080 from anyallow 27017 from anyDB ports exposed globallyNo rate limitingDefault ALLOW = dangerousdefault deny incominglimit 22 from anyallow 443 from anyallow 3306 from 10.0.1.0/24deny 23,135,445DB restricted to app subnetSSH rate-limitedDefault DENY + explicit allow
Side-by-side comparison highlighting the difference between a permissive default-allow setup and a hardened least-privilege UFW configuration.

Secure Your Ubuntu Server with Confidence

When you correctly configure a firewall with UFW on Ubuntu, you establish a foundational security layer that protects against automated threats and limits blast radius during incidents. Remember the core principles: always whitelist SSH before enabling, use default deny for inbound traffic, restrict database ports to specific sources, and understand how Docker interacts with netfilter rules. Document your rule set as part of your compliance evidence—auditors will ask for it. If you need help designing a comprehensive security posture for your Ubuntu infrastructure or preparing for SOC 2 certification, reach out to discuss your environment.

Frequently Asked Questions

Always allow SSH port 22 or your custom port before running ufw enable. Use ufw allow 22/tcp first, then verify with ufw status verbose to confirm the rule exists before activating the firewall to prevent losing remote access.

Deny incoming, allow outgoing.

Run ufw allow from 192.168.1.50 to permit all traffic from that source. For specific ports, append to any port 80 or similar. This creates an allow rule scoped strictly to the designated IP address while maintaining default deny policies for others.

Yes, ensure IPV6=yes in /etc/default/ufw. Reload with ufw reload after editing. UFW automatically generates corresponding IPv6 rules when IPv4 rules are added, provided this configuration setting is enabled and the system has valid IPv6 connectivity configured properly.

List numbered rules using ufw status numbered, identify the target rule number, then run ufw delete followed by that number. Confirm deletion when prompted. This removes only the specified rule without affecting other existing firewall configurations or active connections currently established.

No, established connections persist.

Allow ports 80 and 443 for Nginx or Apache serving Laravel. If using Laravel Reverb or WebSockets, also open the custom WebSocket port. Run ufw allow 'Nginx Full' for combined HTTP/HTTPS access, ensuring your application server binds correctly to permitted interfaces only.

You lose remote access immediately. Recovery requires physical console access or cloud provider VNC to disable UFW via ufw disable. Always verify SSH rules exist in ufw status output before activation. Set up automated backups of working firewall states to avoid repeated lockouts during configuration changes.

Use ufw limit 22/tcp to restrict connections. This allows six attempts per thirty seconds from single sources before temporary blocking occurs. Useful against brute force attacks while permitting legitimate administrative access. Monitor logs at /var/log/ufw.log to tune thresholds based on actual traffic patterns observed.

UFW wraps iptables/nftables for simplicity but lacks advanced features like NAT masquerading or complex chain manipulation. Suitable for standard web servers and application hosts. For Kubernetes nodes, load balancers, or intricate routing requirements, direct nftables or iptables management provides necessary granular control beyond UFW abstraction capabilities.

Review /var/log/ufw.log for denied entries.

Docker manipulates iptables directly, often bypassing UFW. Add rules to /etc/ufw/before.rules before the DOCKER-USER chain or configure Docker to respect UFW by modifying daemon.json. Alternatively, bind containers to localhost and use reverse proxy on UFW-managed ports to maintain consistent firewall enforcement across services.

Yes, if enabled via ufw enable. Rules save automatically to /etc/ufw/user.rules and /etc/ufw/user6.rules. The systemd service ufw.service starts on boot applying saved configuration. Verify persistence by checking systemctl is-enabled ufw returns enabled status after reboots during maintenance windows or unexpected restarts.

Run ufw disable to stop filtering immediately without deleting rules. Test network connectivity freely, then re-enable with ufw enable to restore previous configuration intact. This preserves all defined rules while allowing diagnostic isolation. Avoid leaving firewalls disabled longer than necessary during production incident response procedures.

Deny drops packets silently causing client timeouts. Reject sends ICMP unreachable or TCP reset responses informing senders the port is closed. Use reject for internal networks where immediate feedback aids debugging. Use deny for public-facing interfaces to avoid revealing firewall existence or port states to potential attackers scanning systems.