Status and Enable
Allow SSH before you enable, every time. A default deny with no SSH rule drops your next login, which shows up as a timeout in PuTTY or (see Network error: Connection timed out). Your current session stays up, so use it to fix the rule.
| Command | What 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 verboseAllow 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.
| Command | What 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.
| Command | What 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
| Command | What 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
| Command | What 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.
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.
| Command | What 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.
| Command | What 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.
| Command | What 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
| Command | What 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.
| Command | What 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/tcpReload and Reset
| Command | What 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:
| File | Contains |
|---|---|
| , | 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.
| Fix | How |
|---|---|
| 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 .
| Command | What 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.
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.
- 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.