Quick answer: allow SSH first, then switch the firewall on:
sudo ufw allow OpenSSH
sudo ufw enable
sudo ufw status verbose
UFW (Uncomplicated Firewall) is a friendly front end for the Linux kernel firewall. It ships with Ubuntu and is available on Debian and most derivatives. This guide covers the handful of commands that handle 95 percent of real server work.
Before anything else: do not lock yourself out
The order of operations matters. If you enable UFW before allowing SSH, your session may be the last one that ever connects. Always run sudo ufw allow OpenSSH (or sudo ufw allow 22/tcp, or your custom SSH port) before sudo ufw enable. If you do get locked out, your provider's web console gives you a way back in without SSH.
Check where you stand
sudo ufw status verbose
Shows whether UFW is active, the default policies, and every rule. Status: inactive means the kernel firewall is untouched and everything is allowed.
Default policies
The standard posture is: refuse unsolicited inbound traffic, allow all outbound:
sudo ufw default deny incoming
sudo ufw default allow outgoing
These are the defaults on most installs anyway, but stating them explicitly makes the config self-documenting. Everything you actually serve becomes an explicit allow rule on top of this.
Allowing services
By port and protocol:
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
By application profile (packages register profiles; list with sudo ufw app list):
sudo ufw allow OpenSSH
sudo ufw allow "Nginx Full"
A bare port number without /tcp or /udp opens both protocols; be specific when you can.
Restricting by source
Open a port only to a specific address or network instead of the whole internet:
sudo ufw allow from 203.0.113.10 to any port 22 proto tcp
sudo ufw allow from 10.0.0.0/24 to any port 5432 proto tcp
The second form is the classic database pattern: PostgreSQL reachable from your private network, invisible to everyone else. For services that should never be public at all, an even better firewall rule is no rule: bind the service to 127.0.0.1 or a private interface in its own config.
Denying and deleting
sudo ufw deny 8080/tcp
To remove rules, the numbered view is the least error-prone:
sudo ufw status numbered
sudo ufw delete 3
Numbers shift after each deletion, so re-run status numbered between deletes. You can also delete by repeating the original rule: sudo ufw delete allow 8080/tcp.
Rate limiting: the SSH sweet spot
sudo ufw limit ssh
limit allows a connection unless the same address has attempted 6 or more connections in the last 30 seconds, then it blocks. It is the lightest possible brake on brute-force attempts and works well combined with key-only SSH authentication.
Common recipes
- Web server:
ufw allow 80/tcp,ufw allow 443/tcp, keep SSH limited. - Custom SSH port: if
sshdlistens on 2222, allow2222/tcpbefore removing the port 22 rule, and test a fresh login in a second terminal first. - Game or app server: allow the exact port and protocol from the application docs; many use UDP, which a bare
/tcprule will not cover. - Second admin location: add a second
allow fromrule for the new address; rules are additive.
Know what is actually listening
Firewall rules should mirror reality. Before writing them, see which services are listening and on which addresses:
sudo ss -tlnp
Anything bound to 0.0.0.0 or [::] is reachable from every interface and needs either a UFW rule or a config change to bind it to 127.0.0.1. Anything already bound to localhost needs no firewall attention at all. Re-run this after installing new software; plenty of packages start a listener on install without asking.
The Docker caveat
One honest warning: containers published with Docker's -p flag insert their own kernel firewall rules ahead of UFW's, so a port can be open to the world even though UFW never allowed it. If you run Docker, do not assume UFW's status output is the whole story: check from outside with a port scan from another machine, bind containers to localhost (-p 127.0.0.1:8080:8080) when they sit behind a reverse proxy, and read up on Docker's iptables integration before exposing anything sensitive.
Logging and IPv6
sudo ufw logging on writes blocked-connection entries to the system log (view with journalctl or in /var/log/ufw.log); logging medium is chattier. IPv6 is covered automatically as long as IPV6=yes is set in /etc/default/ufw, which is standard; each rule you add creates a v4 and a v6 entry.
Starting over
sudo ufw reset
Disables the firewall and wipes every rule so you can rebuild cleanly. Follow immediately with the allow-SSH-then-enable sequence from the top of this guide.
A final habit worth keeping: after any firewall change, open a new terminal and confirm you can still reach SSH and your public services before you close the session that made the change.