UFW firewall on Ubuntu: a safe setup over SSH, step by step
To set up a UFW firewall on Ubuntu, deny incoming traffic by default, allow SSH first and only then run sudo ufw enable. Every other port stays closed until a rule opens it. The order matters on a remote server: with incoming traffic denied and no SSH rule in place, turning the firewall on can cut off the very session that issued the command.
ufw is disabled until it is enabled. The ufw(8) manual page for Ubuntu 24.04 LTS says that after installation it has a default incoming policy of deny, a default forward policy of deny and a default outgoing policy of allow. Everything below follows that page (ufw 0.36.2-6). The pages for 22.04 LTS (ufw 0.36.1) and 26.04 LTS (ufw 0.36.2-9) match for the commands used here.
Avoid an SSH lockout first
The manual warns that ufw enable flushes the firewall chains and may drop existing connections such as SSH. Its remedy is to add the SSH rule before enabling, so that the port is open as soon as the firewall is on. When the command runs inside an SSH session, ufw asks for confirmation. The option --force suppresses that prompt, so leave it out.
- Find the real SSH port.
sudo ufw show listeninglists the TCP ports in the listening state together with the executable behind each one. - Preview a rule. Adding
--dry-run, for examplesudo ufw --dry-run allow http, prints the resulting rules without applying them. - Keep a way back. Leave the current session open and test a login from a second terminal first. A provider console or rescue mode is the fallback if both fail.
Default deny, SSH first, then each service
- Check the state.
sudo ufw statusprintsStatus: inactivewhile the firewall is off. - Set the policies with
sudo ufw default deny incomingandsudo ufw default allow outgoing. Both match the installation defaults, so they mainly record intent. Run them before enabling, because changing the default policy on a running firewall flushes the chains. - Allow SSH with
sudo ufw allow OpenSSH. The openssh-server package in Ubuntu ships a ufw profile of that name for port 22/tcp, andsudo ufw app info OpenSSHshows what it covers. If sshd listens on another port, name the port instead, for examplesudo ufw allow 2222/tcp. To rate-limit SSH, use the rule from the next section in place of this one. - Open the ports of the services on the server, for example
sudo ufw allow 80/tcpandsudo ufw allow 443/tcp. A rule without a protocol, such assudo ufw allow 53, covers both TCP and UDP. - Enable the firewall with
sudo ufw enable. The command also turns the firewall on at boot. - Check the result with
sudo ufw status verbose, which adds the logging level and the default policies to the list of rules, then log in from a second terminal before closing the first.
To let only one machine reach SSH, the Ubuntu Server documentation gives sudo ufw allow proto tcp from 192.168.0.2 to any port 22, and 192.168.0.0/24 in place of the address allows a whole subnet. If your own address changes, such a rule locks you out.
Limit SSH login attempts
ufw supports connection rate limiting, which the manual describes as useful against brute-force login attacks. A limit rule normally allows the connection but denies an IP address that tries to initiate 6 or more connections within 30 seconds:
sudo ufw limit ssh/tcp
Rule order decides the outcome: the first match wins, and the manual advises adding specific rules before general ones. An earlier allow rule for port 22 therefore matches first, and the limit is never reached. Use one or the other. If you already ran sudo ufw allow OpenSSH, remove it with sudo ufw delete allow OpenSSH and add the limit rule.
List, insert and delete rules
| Task | Command | What the documentation says |
|---|---|---|
| List with numbers | sudo ufw status numbered | In the output, Anywhere means any address, IPv4 or IPv6 |
| Delete by number | sudo ufw delete 3 | The number is the one shown by status numbered |
| Delete by rule | sudo ufw delete allow 80/tcp | Prefix the original rule with delete |
| Insert at a position | sudo ufw insert 1 allow 80 | Adds the new rule at that number |
| Turn logging on | sudo ufw logging on | Default level is low; with rsyslog, entries may also go to /var/log/ufw.log; levels above medium can fill the disk |
| Start over | sudo ufw reset | Disables the firewall and restores installation defaults; --force skips the confirmation |
Three details catch people out. Rule numbers shift: per the Ubuntu wiki, deleting rule 1 moves the later rules up, so list again before the next deletion. With IPv6 enabled, deleting a generic rule such as ufw allow 22/tcp by number removes only that entry, while deleting it by rule text removes both the IPv4 and IPv6 rule. And a multi-port rule such as ufw allow proto tcp from any to any port 80,443 is changed as a whole: the manual says deleting just 443 later is not possible.
IPv6 uses the same rules
The manual states that IPv6 is allowed by default. With it enabled, rules are written like IPv4 rules and appear in the output of ufw status, and a rule that names no address matches both versions, so ufw allow 22 opens port 22 for IPv4 and IPv6. An IPv6-only rule names an IPv6 address, for example sudo ufw deny proto tcp from 2001:db8::/32 to any port 25.
IPv6 firewalling only works when it is enabled in /etc/default/ufw. Setting IPV6 to no there and reloading ufw leaves IPv6 traffic accepted only on the loopback interface, so check that line first.
Docker ignores ufw for published ports
Docker's documentation says that Docker and ufw use firewall rules in ways that make them incompatible. Traffic to a published container port is diverted in the nat table before it reaches the INPUT and OUTPUT chains that ufw uses, so a rule such as ufw deny 8080 does not hide a container started with -p 8080:80.
The Docker page on port publishing notes that adding the loopback address to the publish flag makes the port reachable only from the Docker host:
docker run -p 127.0.0.1:8080:80 -p '[::1]:8080:80' nginx
The same page warns that in releases older than Docker 28.0.0, hosts in the same L2 segment can still reach ports published to localhost. A web server on the host that proxies to that port is then the only service that needs a ufw rule.
Common questions
Is UFW enabled by default on Ubuntu?
No. On installation ufw is disabled, with a default policy of deny for incoming and forwarded traffic and allow for outgoing traffic (ufw(8), Ubuntu 24.04). It starts filtering only after sudo ufw enable.
How do I avoid locking myself out when enabling ufw?
Add the SSH rule before enabling, for example sudo ufw allow OpenSSH. Enabling flushes the chains and can drop existing connections. Keep one session open and test a second login first.
What does ufw limit do?
A limit rule normally allows a connection but denies an IP address that tries to initiate 6 or more connections within 30 seconds. The manual gives sudo ufw limit ssh/tcp as the typical use against brute-force logins.
How do I delete a ufw rule?
List the rules with sudo ufw status numbered, then run sudo ufw delete 3, or prefix the original rule with delete, for example sudo ufw delete allow 80/tcp. With IPv6, deleting by rule text removes both entries.
Does ufw block ports published by Docker?
Not reliably. Docker diverts traffic to published container ports in the nat table before it reaches the chains ufw uses. Publishing to 127.0.0.1, as in -p 127.0.0.1:8080:80, keeps the port reachable from the host only.
More guides

SSH config file: set up ~/.ssh/config once and connect to every server by name
SSH config file explained: Host blocks, IdentityFile, ProxyJump, keepalives, connection sharing, which value wins and how to debug with ssh -G and ssh -v.