Cheat Sheet

UFW Commands

UFW (Uncomplicated Firewall) is the front end for iptables that Ubuntu and Debian ship, and the firewall most single-server setups use. This sheet lists the ufw commands for enabling it without locking yourself out, opening and closing ports, allowing an IP, deleting rules, rate limiting SSH, and the Docker problem everyone hits once.

Last updated August 29, 2026

Status and Enable

Allow SSH before you enable, every time. See SSH Lockout below for the order and the way out.

CommandWhat it does
Active or inactive, plus the rule list
Adds default policies, logging level, and profiles
Rules with the numbers you need for and
Open SSH using the app profile (do this first)
Turn the firewall on and start it at boot
Same, without the "may disrupt SSH" confirmation (scripts)
Turn it off; rules are kept
Rules you have added, including while inactive
Listening ports and which rule, if any, covers each
The underlying iptables rules ufw wrote
Installed version

A safe first setup:

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 80,443/tcp
sudo ufw enable
sudo ufw status verbose

SSH Lockout

puts into force the moment you run it. The session you are typing in survives, because connection tracking counts it as established, but the next login is dropped unless a rule already allows the port. Add the SSH rule first and enable last.

sudo ufw allow OpenSSH          # or: sudo ufw allow 22/tcp
sudo ufw show added             # check the rule is there while ufw is still off
sudo ufw enable
CommandWhat it does
The rule that has to exist before , every time
Lists rules while ufw is inactive, so you can verify before enabling
Deadman switch: turns the firewall off in 5 minutes if you lose the session
Find the pending job; cancels it
from a second terminalTest with a fresh connection and keep the old one open
What to run from the provider's serial or web console if you are already out

Cancel the deadman switch once the new login works. If sshd listens on a non-standard port, allow that port instead and check it from outside with the SSH port checker. A default shows up as a timeout (Network error: Connection timed out), a default as connection refused.

Allow Rules

A port with no protocol opens both TCP and UDP. Service names come from , app names from . The firewall rule generator writes these for you.

CommandWhat it does
Open a TCP port
Open a port for TCP and UDP
Same as , by service name
Several ports in one rule
A port range
UDP only (WireGuard)
An app profile; quote names with spaces
Only on one interface
Everything arriving over an interface (Tailscale, for WireGuard)
An outbound rule, only useful after
Long form; the same rule as
Attach a comment that shows in

Deny and Reject

drops packets silently, so the client waits until it times out. sends a refusal back, so the client fails at once with "connection refused". Deny is the usual choice for the internet, reject is kinder on internal networks.

CommandWhat it does
Block a port
Block every packet from one address
Block a subnet
Block one address on one port
Refuse instead of drop
Stop outbound mail from this box
Block all inbound on one interface

Deny rules only work when they sit above the allow rule they override; see Rule Order below.

Allow From an IP or Subnet

CommandWhat it does
Everything from one address
Everything from a subnet
SSH from one address only
SSH from one subnet
MySQL from the private network
To a specific local address, when the server has several
The IPv6 version; needs (see below)

rules and plain port rules combine. Allowing 22 from a subnet and then also opens it to everyone, so check after each change.

Delete Rules

CommandWhat it does
Get the number
Delete rule 3 (asks for confirmation)
Delete without the prompt
Delete by repeating the rule
Works for long rules too; spell it exactly as added
Delete an app profile rule

Numbers renumber after each delete. To remove rules 2, 5, and 7, delete 7 first, then 5, then 2. With IPv6 on, every rule appears twice in , once plain and once marked , and each copy carries its own number.

Rule Order and Insert

ufw evaluates rules top to bottom and the first match wins. A added after never fires for port 22, because the allow already matched.

CommandWhat it does
Put a rule at position 1, above everything else
Same as (ufw 0.36 and newer)
Insert at a specific position
Confirm where it landed

With IPv6 on, an position counts IPv4 and IPv6 rules together; ufw refuses a position that would split them and tells you so.

Default Policies

Defaults apply to anything no rule matches. The normal shape is deny in, allow out.

CommandWhat it does
Drop anything not explicitly allowed (the sensible default)
Let the server make outbound connections
Refuse instead of drop
Lock down outbound; then you need rules for DNS (53), HTTP, updates
Drop forwarded traffic (default); when the box is a router or VPN gateway
Shows the current defaults on the "Default:" line

Rate Limiting

allows a port but blocks an address that opens 6 or more connections within 30 seconds. It is the cheapest brute-force defence for SSH, and it costs nothing when you also use keys.

CommandWhat it does
Rate-limit SSH ( is the same)
Same, via the app profile
SSH on a non-standard port
Remove it

A rule replaces the rule for that port; you do not need both. For anything beyond 6 hits per 30 seconds, use fail2ban.

Logging

CommandWhat it does
Log blocked packets (the level)
Stop logging
Also log allowed packets that match a rule, rate limited
Log everything, rate limited; noisy
Everything, no rate limit; only for short debugging
Watch it (Ubuntu writes to this file via rsyslog)
The same messages from the kernel log on systemd-only boxes
Recent blocked packets, with SRC, DST, DPT fields
Log new connections matching one rule only ( logs every packet)

App Profiles

Packages such as OpenSSH, nginx, Apache, and Samba drop a profile in that names their ports. Rules made from a profile update when the package updates.

CommandWhat it does
Profiles available on this server
The ports a profile opens
Ports 80 and 443 ( is 80 only, 443 only)
Use a profile in a rule
Reload a profile after its file changed ( to add rules for it)

A profile of your own goes in :

[MyApp]
title=My App
description=Internal API
ports=8080,8443/tcp

Ping and ICMP

ufw's rule syntax has no ICMP protocol, so fails with . Ping is accepted in before your own rules run, and changing that means editing the file.

CommandWhat it does
Find the ICMP lines, above the filter table's
Apply an edit to ; file changes are not live until you do
Ignore pings kernel-wide instead, no firewall edit
Where to add to make that survive a reboot
Confirm the change from another host

To stop answering ping, change to on the echo-request line in , and in for IPv6:

-A ufw-before-input -p icmp --icmp-type echo-request -j DROP

Leave the other ICMP types accepted. carries path MTU discovery, and dropping it breaks large packets over some links.

Port Forwarding and NAT

The ufw CLI covers the INPUT chain. Forwarded traffic needs rules, IP forwarding turned on, and a NAT block written by hand.

CommandWhat it does
Allow forwarding between two interfaces
Allow forwarding to one host and port
The forward rules, with numbers for
Remove one
Uncomment ; nothing routes without it
Leave it and use rules; forwards everything

A DNAT port forward goes at the top of , above the line:

*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to-destination 10.0.0.5:8080
-A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE
COMMIT

Then , and add a matching rule, because the rewritten packet still has to pass the FORWARD chain. Traffic from the server itself never goes through PREROUTING, so testing a forward from the box always fails; test from another host or with the SSH port checker.

Reload and Reset

CommandWhat it does
Re-read the rule files without dropping connections; needed after editing files under
Full restart, including the IPv6 setting
Disable, delete every rule, and restore defaults; backs up the old files to
Reset without confirmation
The service that applies rules at boot

Rules added with , , and apply immediately; no reload needed. The files, if you ever need them:

FileContains
, The rules you added, generated by ufw; do not hand-edit
, Rules run before yours; NAT and masquerade go here
, Rules run after yours; the ufw-docker block lives here
, default policies,
Kernel settings ufw applies, such as

Anything ufw's syntax cannot express (NAT, port forwarding, filtering) goes in or as raw iptables. The iptables to nftables converter helps if you are moving that block elsewhere.

Docker

Docker bypasses ufw. It writes its own iptables rules to publish ports, and traffic to a container goes through the FORWARD chain, not INPUT, so a container is open to the world even with . will not show it.

FixHow
Bind to localhost (or in compose), then reverse proxy through nginx or Caddy
ufw-docker, then and
Allow a container after ufw-docker (container name, port)
Allow with a route rule after ufw-docker
Disable Docker's iptables in ; breaks container-to-internet NAT unless you add it yourself, so not recommended

Docker Desktop and rootless Docker do not publish through iptables this way, so the problem is specific to the standard Linux engine. Full container commands are on the Docker cheat sheet.

IPv6

ufw handles IPv6 when is set in , which is the default on Ubuntu. With it on, every rule you add applies to both stacks and lists each rule twice, the second marked .

CommandWhat it does
Check the setting
Turn it on
Apply the change; is not enough for this one
An IPv6-only rule
Just the IPv6 rules

If the server has an IPv6 address and , that address has no firewall at all.

Install

Ubuntu Server ships ufw. Debian, Raspberry Pi OS, and minimal cloud images do not.

CommandWhat it does
Debian and Ubuntu
Check whether it is already there before installing
Arch
Fedora, and RHEL clones with EPEL, where firewalld is the default instead
Start it at boot where the package does not do that for you (Arch)
The desktop GTK front end
is off a plain user's PATH, which is most reports

Gotchas

  • Enabling without an SSH rule is the classic lockout. first, then . If it already happened and the provider has a web console, disable ufw from there; a policy shows as connection refused, a policy as a timeout.
  • opens TCP and UDP. Nothing listens on UDP 22, so it is harmless, but is what you meant.
  • Docker ignores ufw. See the Docker section; saying only 22 is open does not mean 8080 is closed.
  • First match wins. A added after a matching does nothing; use for blocks.
  • keeps your rules and deletes them. says in both cases; tells them apart.
  • means the package is missing (Debian and minimal images) or you left off , since is not on a normal user's PATH.
  • Inactive again after a reboot happens on Arch and on images where the systemd unit was never enabled. fixes it; on Ubuntu, already does.
  • ICMP is not expressible in a ufw rule. Ping is allowed in , so does not stop it.
  • on a distribution that uses firewalld (Rocky, Fedora) or on a box with a cloud provider firewall in front is a second layer. Two firewalls with different rules is the usual reason a port that ufw says is open still times out.
  • Editing by hand gets overwritten by the next command. Use the CLI for rules and the and files for the raw iptables ufw cannot express. See the SSH cheat sheet for the side of testing a port after a change.

UFW FAQ

Will ufw block SSH when I enable it?

Not the session you are already in. Connection tracking treats it as established, so it survives sudo ufw enable even though ufw warns that enabling may disrupt existing ssh connections. The next login is the one that gets dropped, and only when no rule allows the port. Run sudo ufw allow OpenSSH (or sudo ufw allow 22/tcp if the app profile is missing), confirm it with sudo ufw show added, then enable. If sshd listens somewhere other than 22, allow that port instead, and if you always connect from the same network, sudo ufw allow from 203.0.113.0/24 to any port 22 proto tcp is tighter than opening 22 to everyone.

Why does ufw status say inactive?

Because nothing has run sudo ufw enable since the last boot. Rules added while it is off are kept, and sudo ufw show added lists them, so an inactive status does not mean an empty rule set. Two causes beyond simply forgetting: on Arch and on minimal Debian images the package does not enable the systemd unit, so ufw is inactive again after a reboot until you run sudo systemctl enable ufw; and sudo ufw reset switches it off and deletes the rules, unlike sudo ufw disable, which keeps them. A plain user gets an error instead of the status, because ufw needs root and /usr/sbin is not on a normal user's PATH.

Are ufw rules persistent after a reboot?

Yes. Every allow, deny, and limit you add is written to /etc/ufw/user.rules (and user6.rules for IPv6) the moment you run it, and ufw.service reloads them at boot. Nothing else is needed, which is the main reason people pick ufw over raw iptables, where rules vanish at reboot unless you save them. The one way to lose them is sudo ufw reset, which backs the files up with a timestamp suffix in /etc/ufw and then empties them. sudo ufw disable does not delete rules; sudo ufw show added lists them even while the firewall is off.

Can I use ufw with Docker?

Yes, but not the way you would expect. Docker writes its own iptables rules to publish container ports, in the DOCKER and FORWARD chains, and packets to a published port never pass through the INPUT chain where ufw's rules live. So a container started with -p 8080:80 is reachable from the internet even when ufw shows only port 22 open. Two fixes work: bind the port to localhost (-p 127.0.0.1:8080:80) and put nginx or Caddy in front, or install the ufw-docker script, which adds a filter to the DOCKER-USER chain so ufw route allow rules govern container traffic. Setting iptables to false in daemon.json also stops the bypass but breaks container networking in other ways, so most people avoid it.

Does ufw block ping?

Not by default. The rules in /etc/ufw/before.rules accept ICMP echo-request before any of your own rules run, so a server with default deny incoming still answers ping. The CLI cannot change that, because ufw's rule syntax has no icmp protocol and sudo ufw deny proto icmp returns ERROR: Unsupported protocol icmp. To stop replying, change the echo-request line in /etc/ufw/before.rules from ACCEPT to DROP, do the same in before6.rules for IPv6, then run sudo ufw reload. Leave the other ICMP types accepted: destination-unreachable carries path MTU discovery, and dropping it breaks large packets on some links.

Related cheat sheets