How to Set Up a Firewall (UFW) and Fail2Ban on Ubuntu
To secure an Ubuntu server, enable UFW and Fail2Ban together. Set UFW to deny all incoming traffic by default and allow only SSH, HTTP and HTTPS. Then install Fail2Ban with an sshd jail so that IP addresses with repeated failed logins are banned automatically. This guide covers both tools on a Cloud VPS or dedicated server running Ubuntu 24.04 or 22.04 with root or sudo access. If you use cPanel/WHM, see the section on CSF/LFD instead.
Prerequisites
- An Ubuntu 24.04 or 22.04 server, for example a Cloudesta Cloud VPS.
- SSH access as root or as a user with
sudo. - The SSH port your server actually uses (22 unless you changed it).
- Your own public IP address, so you can whitelist it in Fail2Ban.
Why do you need a firewall on a brand-new VPS?
A fresh VPS has a public IPv4 address. Automated scanners usually find it within minutes and start guessing SSH passwords. Every service you install later may also listen on the network, including databases, Redis and admin panels. A default-deny firewall blocks anything you did not open on purpose. Fail2Ban then stops brute-force attempts against the ports you did open.
1. How do I install UFW and set default policies?
UFW comes preinstalled on Ubuntu but is inactive. Make sure it is present, then set the default policies:
sudo apt update
sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
These policies take effect only when you enable UFW in step 4. IPv6 is covered automatically because IPV6=yes is the default in /etc/default/ufw.
2. How do I allow SSH without locking myself out?
Always allow SSH before you enable the firewall. First, confirm which port SSH uses:
sudo sshd -T | grep -i '^port'
Then allow that port. Use the first command for the default port 22. If you use a custom port, such as 2222, use the second command instead:
sudo ufw allow 22/tcp
# or, for a custom port:
sudo ufw allow 2222/tcp
OpenSSH application profile (ufw allow OpenSSH) only opens port 22. If you moved SSH to another port, allow that number explicitly. Keep your current SSH session open until you have confirmed that a new session can connect.3. How do I open ports 80 and 443 for a website?
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
If your web server supports HTTP/3, also run sudo ufw allow 443/udp. You can restrict a service to a single address, for example MySQL from one application server:
sudo ufw allow from 203.0.113.10 to any port 3306 proto tcp
4. How do I enable UFW and check its status?
sudo ufw enable
sudo ufw status verbose
UFW warns that enabling may disrupt existing SSH connections. Answer y only after you have added the SSH rule. The status output should show Status: active, deny (incoming), allow (outgoing), and your allowed ports.
5. How do I delete a UFW rule?
List the rules with numbers, then delete a rule by its number or by its exact definition:
sudo ufw status numbered
sudo ufw delete 3
# or
sudo ufw delete allow 80/tcp
Rule numbers shift after each deletion, so run status numbered again before deleting another rule.
6. How do I rate-limit SSH with UFW?
The limit action denies an IP address that opens 6 or more connections within 30 seconds. Running it on a port that already has an allow rule updates that rule:
sudo ufw limit 22/tcp
sudo ufw status
The SSH line should now show LIMIT. Tools that open many connections in quick succession, such as Ansible or scripted scp, can trigger this limit. In that case, keep a plain allow rule and rely on Fail2Ban.
7. How do I install and configure Fail2Ban?
sudo apt install -y fail2ban python3-systemd
Do not edit jail.conf, because package updates overwrite it. Put your own settings in /etc/fail2ban/jail.local instead:
sudo nano /etc/fail2ban/jail.local
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
ignoreip = 127.0.0.1/8 ::1 198.51.100.25
[sshd]
enabled = true
port = 22
backend = systemd
The settings work like this:
maxretrysets how many failures are allowed within thefindtimewindow before an IP is banned.bantimesets how long the ban lasts.ignoreiplists addresses that are never banned. Replace198.51.100.25with your own IP.portmust match your SSH port, for exampleport = 2222if you changed it.backend = systemdreads SSH logs from the journal. This avoids log path problems.
Test the configuration, then enable and restart the service:
sudo fail2ban-client -t
sudo systemctl enable fail2ban
sudo systemctl restart fail2ban
8. How do I check Fail2Ban status and unban an IP?
sudo fail2ban-client status
sudo fail2ban-client status sshd
sudo fail2ban-client set sshd unbanip 198.51.100.25
The status sshd output shows the number of failed attempts and the list of currently banned IPs.
What if my server runs cPanel/WHM?
Do not install UFW or Fail2Ban on a cPanel server. cPanel servers typically use CSF (ConfigServer Security & Firewall) with its login failure daemon, LFD, often alongside cPHulk or Imunify360. Running two firewalls at once causes conflicts.
If LFD has blocked your IP and you have root access to WHM from another connection, unblock it in either of these ways:
- In WHM, go to Plugins → ConfigServer Security & Firewall. Enter the IP under Search for IP or Quick Unblock.
- From SSH, run
csf -g 198.51.100.25to find the block, thencsf -dr 198.51.100.25to remove it.
Also check Security Center → cPHulk Brute Force Protection, because cPHulk keeps its own block list. On Cloudesta shared hosting, the firewall is managed by us. Open a support ticket and include your public IP so we can unblock it.
Verify it works
Open a new SSH session to confirm you can still log in. Then check ports from your local computer:
nc -zv 203.0.113.10 22
nc -zv 203.0.113.10 443
nc -zv -w 3 203.0.113.10 3306
Ports 22 and 443 should report success, and 3306 should time out. On the server, confirm that Fail2Ban is reading the journal and recording failures:
sudo fail2ban-client status sshd
sudo tail -n 20 /var/log/fail2ban.log
On a public server, Total failed usually starts rising within an hour. If you want to trigger a ban yourself, test from an IP that is not in ignoreip, such as a mobile hotspot.
Common problems
I enabled UFW and now SSH times out
The SSH port was not allowed, or you allowed 22 while SSH listens on a custom port. You will need console access to fix it. Open a support ticket and we will arrange console or rescue access. From the console, run sudo ufw allow 2222/tcp (using your real port) or sudo ufw disable.
I banned my own IP with Fail2Ban
Connect from another network or through the console, then run sudo fail2ban-client set sshd unbanip YOUR_IP. Add your IP to ignoreip so it does not happen again.
Fail2Ban runs but never bans anyone
This is usually a log source problem. If you use the file backend, logpath must point to a file that exists, normally /var/log/auth.log. Minimal images without rsyslog do not have that file. Use backend = systemd as shown above and make sure python3-systemd is installed. Also check that the jail's port matches your SSH port.
A Docker container is reachable even though UFW blocks the port
Docker writes its own iptables rules for published ports, and these are evaluated before UFW's rules. To fix this, bind container ports to localhost and expose them only through a reverse proxy on 80/443. For example, in docker-compose.yml use "127.0.0.1:5678:5678" instead of "5678:5678". This matters for self-hosted apps such as those on n8n hosting.
Frequently asked questions
Do UFW and Fail2Ban conflict with each other?
No. Fail2Ban adds its own ban chain to the kernel firewall, and that chain works alongside UFW's rules. You can also set banaction = ufw if you prefer bans to appear in ufw status.
Is changing the SSH port enough instead of Fail2Ban?
A custom port reduces log noise but does not stop targeted attacks. Use SSH keys, a firewall and Fail2Ban together.
How do I make bans last longer for repeat offenders?
Add bantime.increment = true to the [DEFAULT] section of jail.local. Each repeat ban then lasts longer than the previous one. Restart Fail2Ban after making the change.
Need help? If you are locked out or need console or rescue access, open a ticket at https://cloudesta.com/submitticket.php and include your server IP.