UFW, short for Uncomplicated Firewall, is a command-line frontend for the netfilter packet filter in Linux that translates short, readable commands such as ufw allow 22/tcp into the underlying iptables or nftables rules. In Linux networking it serves as the default host firewall tool on Ubuntu and several related distributions, aimed at administrators who need a stateful, default-deny policy on a single machine without writing raw rule chains.
UFW does not filter packets itself. The kernel’s netfilter subsystem does the filtering; UFW manages rule files, loads them at boot and offers a consistent syntax for the most common tasks: opening ports, restricting sources, rate limiting and logging.
How UFW builds rules on top of netfilter
UFW works as a rule generator and loader. When an administrator adds a rule, UFW writes it to /etc/ufw/user.rules (and user6.rules for IPv6) and inserts it into its own dedicated chains, such as ufw-user-input, which hang off the standard INPUT, OUTPUT and FORWARD hooks. On current distributions the iptables command is usually the iptables-nft compatibility layer, so UFW rules end up as nftables objects even though UFW still speaks iptables syntax.
Around the user rules sit fixed rule files that the administrator can edit directly. before.rules runs first and accepts established and related connections, loopback traffic and essential ICMP messages; after.rules runs last; the default policies apply to anything left unmatched. Because the generated rules use connection tracking, a single allow rule for inbound port 443 also admits the reply packets of that connection automatically.
The table below lists the files that define UFW behavior and what each one controls.
| File | Purpose | Typical edit |
|---|---|---|
| /etc/default/ufw | Global settings and default policies | IPV6=yes, forward policy |
| /etc/ufw/ufw.conf | Enabled state and log level | ENABLED=yes, LOGLEVEL=low |
| /etc/ufw/before.rules | Rules evaluated before user rules | NAT, ICMP handling |
| /etc/ufw/user.rules | Rules created with ufw commands | Not edited by hand |
| /etc/ufw/after.rules | Rules evaluated after user rules | Late catch-all logging |
| /etc/ufw/applications.d/ | Application profiles | Named port sets per service |
Core commands, policies and rule options
A UFW setup starts with default policies: the usual baseline is ufw default deny incoming and ufw default allow outgoing, which blocks unsolicited inbound traffic while letting the host initiate connections. Individual rules then punch precise holes in that baseline, by port, protocol, source address, interface or application profile.
Rules are evaluated in order, first match wins, so a broad allow placed above a narrow deny makes the deny useless. ufw status numbered shows the order, and ufw insert 1 places a rule at the top. The following table summarizes the options that administrators use most often.
| Command | Effect | Note |
|---|---|---|
ufw allow 22/tcp | Opens TCP port 22 from anywhere | Applies to IPv4 and IPv6 |
ufw allow from 10.0.0.0/24 to any port 5432 | Opens a port for one subnet | Preferred for databases |
ufw limit ssh | Allows with rate limiting | Blocks 6 or more connections in 30 seconds |
ufw deny out 25/tcp | Blocks outbound SMTP | Useful against spam relays |
ufw route allow in on eth1 out on eth0 | Permits forwarded traffic | Needs IP forwarding enabled |
ufw delete 3 | Removes rule number 3 | Check numbering first |
ufw logging medium | Raises log verbosity | Levels: off, low, medium, high, full |
Application profiles in /etc/ufw/applications.d give names such as “OpenSSH” or “Nginx Full” to sets of ports, so ufw allow "Nginx Full" opens ports 80 and 443 together. Adding comment 'reason' to a rule records why the hole exists, which pays off during later audits.
UFW compared with nftables, iptables and firewalld
UFW sits at the simplest end of the Linux firewall stack. Raw nftables and iptables expose every table, chain and match, while UFW hides them behind a host-centric vocabulary. The comparison below shows where each tool fits; nftables and iptables have their own pages and are summarized only briefly here.
| Tool | Abstraction level | Typical use | Main limitation |
|---|---|---|---|
| UFW | High, port and source rules | Single servers, desktops | Little support for complex routing |
| firewalld | High, zones and services | Fedora, RHEL family hosts | Zone model adds learning curve |
| nftables | Low, full rule language | Routers, complex policies | Verbose for simple tasks |
| iptables | Low, legacy rule chains | Older systems and scripts | Separate IPv4 and IPv6 tools |
Running two firewall managers at once is a classic mistake: UFW and firewalld both rewrite netfilter state and can silently override each other. A host should have exactly one tool in charge, and hand-written nftables rules should go into UFW’s before or after files rather than into a separate service.
Enabling UFW safely on a remote server
The most important rule for remote administration is to allow SSH before enabling the firewall. Because the default incoming policy is deny, running ufw enable over an SSH session without an SSH rule can lock the administrator out of the machine. UFW warns that the command may disrupt existing SSH connections for this reason.
- Set the defaults with
ufw default deny incomingandufw default allow outgoing. - Add the management access rule, ideally
ufw limit OpenSSHor a rule restricted to an admin subnet. - Add the service ports the host actually serves, such as 80 and 443.
- Run
ufw enable, then verify withufw status verbosefrom a second session before closing the first. - Keep console or out-of-band access available on critical machines in case a rule goes wrong.
Rules persist across reboots because the ufw service reloads them at startup. ufw reload reapplies the files after manual edits, and ufw reset returns to an empty, disabled state while saving backups of the old rule files.
Pitfalls and limitations of UFW
The best-known surprise involves Docker and published container ports. Docker inserts its own NAT and filter rules, so traffic to a published port is forwarded before it reaches UFW’s input chains, and a port that ufw status shows as closed can still be reachable from the internet. The usual remedies are binding containers to 127.0.0.1, filtering in the DOCKER-USER chain or adding forward rules in after.rules.
| Symptom | Likely cause | Fix |
|---|---|---|
| Container port open despite deny | Docker rules bypass UFW input chains | Filter in DOCKER-USER or bind locally |
| Deny rule has no effect | Earlier allow rule matches first | Use ufw insert 1 for the deny |
| IPv6 traffic unfiltered | IPV6=no in /etc/default/ufw | Set IPV6=yes and reload |
| SSH lost after enable | No allow rule for port 22 | Recover via console, add rule |
| Forwarded traffic dropped | Default forward policy is DROP | Add ufw route rules |
UFW is a host firewall, not a network design tool. It handles NAT and routing only through hand-edited rule files, offers no zone concept and does not inspect application payloads. For gateways with many interfaces or complex policy routing, writing nftables directly or using a dedicated firewall distribution is the cleaner choice.
Understanding it simply
Think of a server as a building with thousands of numbered doors. UFW is the receptionist with a guest list. By default, nobody from outside may enter, but the owner can say “let visitors use door 443” or “only staff from this office may use door 22”, and the receptionist enforces it.
The receptionist does not build walls; the Linux kernel already has them. UFW simply makes it easy to say who may come in in short, readable sentences, so a small mistake in a complicated rulebook is less likely to leave a door open.
Applied in practice
A small company runs its website and a PostgreSQL database on one Ubuntu virtual server. Before launch, the administrator sets deny incoming and allow outgoing as defaults, adds ufw limit OpenSSH, opens “Nginx Full” for ports 80 and 443, and allows port 5432 only from the office subnet 198.51.100.0/24. After ufw enable, a second SSH session confirms access, and ufw status numbered shows five rules with comments.
A week later, the logs show thousands of rejected connection attempts on port 22 and on the database port from unknown addresses. The rate limit stops repeated SSH guessing, and the database never accepts a connection from outside the office. The result is a small, auditable rule set that a colleague can read and understand in under a minute.