Ubuntu Firewall Setup in Less Than 5 Minutes (Plus One Step That Will Lock You Out)
Setting up a Virtual Private Server (VPS) without a firewall is asking for trouble. Ideally, you’d have a dedicated security person to handle all of this, but that’s rarely the case. One thing I always do on a fresh Linux server is configure a firewall. It used to be a real headache, but Ubuntu ships with the Uncomplicated Firewall (UFW), and that changes things considerably.
In this video, we’ll cover what a firewall actually does and how to configure UFW on Ubuntu 24.04, which is the current LTS as of this recording (26.04 is right around the corner, but the commands won’t be much different).
Hello developers, and welcome to the PHP Architect Channel! I’m your host, Scott Keck-Warren. If you’re new here, we cover topics related to the PHP ecosystem. Subscribe so you don’t miss our videos.
What is a Firewall?
A software firewall monitors and controls incoming and outgoing network traffic based on rules you define. I like to think of it as a gatekeeper (it decides which connections to allow and which to block based on things like port number and protocol). Without one, every open port on your server is reachable by anyone on the internet, including the automated scanners that are constantly sweeping for vulnerabilities. Unlike a hardware firewall (a physical device on your network), a software firewall runs directly on the server, giving you control over that specific machine with little to no CPU overhead.
Most operating systems now include a built-in firewall, but it’s not always enabled by default.
Tools like https://www.shodan.io/ make it trivial to find every open port on the internet. A zero-day drops, and suddenly, that forgotten open port is a liability. Keeping only necessary ports open is how you control your attack surface.
What Is UFW?
Linux has a built-in firewall called iptables, and it’s extremely powerful.
The problem is that iptables rules look like this:
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
One rule isn’t bad. Ten or fifteen, and the config becomes unreadable fast, and you need to worry about ordering, or you might disable all network traffic.
UFW sits on top of iptables and lets you write firewall rules in something closer to plain English. It comes pre-installed on Ubuntu, so most of you already have it.
The Gotcha That Will Ruin Your Day
Before we go any further, I need to tell you the single most common mistake people make with UFW. I’ve seen it happen to experienced developers, and I might have done it myself once or twice (I’m not saying either way):
Always allow SSH before you enable UFW.
When you enable UFW, the default behavior is to deny all incoming connections unless an explicit “allow” rule exists.
If you enable it without adding an SSH rule first, you’ve locked yourself out. The only fix is to log into your emergency console and disable UFW, if you can even get to it.
So the rule is simple: SSH first, enable second. Every time.
One more tip: if you’re making complex changes, add a crontab entry to automatically disable UFW every five minutes. Worst case, you’re locked out for five minutes:
*/5 * * * * root ufw disable
Do remember to remove this when you’re done making changes.
Installing and Enabling UFW
Let’s start by checking whether UFW is already on your system:
sudo ufw status
Status: inactive
If the command isn’t found, UFW isn’t installed. You can install it with apt:
sudo apt install ufw
Following our rule from before, we’re going to allow SSH first:
sudo ufw allow ssh
Rules updated
Rules updated (v6)
This creates a rule that allows TCP traffic on port 22 (the default SSH port). You can also write it explicitly with “[port number]/[protocol]”:
sudo ufw allow 22/tcp
Skipping adding existing rule
Skipping adding existing rule (v6)
In our case, we’ve already defined the same rule using allow ssh, so we get told it’s already defined.
Both do the same thing, but you’ll need the explicit form for services that don’t have a named rule like “ssh”.
With SSH safely allowed, now you can cross your fingers and enable the firewall:
sudo ufw enable
Command may disrupt existing SSH connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup.
UFW is now running and will start automatically every time your server boots.
We can run ufw status again to see our defined rules.
sudo ufw status
Status: active
To Action From
-- ------ ----
22/tcp ALLOW Anywhere
22/tcp (v6) ALLOW Anywhere (v6)
Allowing and Denying Ports
Your server is locked down, but only SSH works. Let’s fix that.
Say you’re running a web application with FrankenPHP and MySQL. You need HTTP and HTTPS open to the public, but MySQL should stay locked down.
FrankenPHP doesn’t ship with a UFW application profile the way Nginx and SSH do, so add your rules by port number:
sudo ufw allow 80/tcp
Rule added
Rule added (v6)
sudo ufw allow 443/tcp
Rule added
Rule added (v6)
Add an explicit deny rule for MySQL, too. It’s not strictly necessary since UFW blocks everything by default, but it documents intent and stops someone from accidentally opening it later:
sudo ufw deny 3306/tcp
Rule added
Rule added (v6)
Verify with:
sudo ufw status
Status: active
To Action From
-- ------ ----
22/tcp ALLOW Anywhere
80/tcp ALLOW Anywhere
443/tcp ALLOW Anywhere
3306/tcp DENY Anywhere
22/tcp (v6) ALLOW Anywhere (v6)
80/tcp (v6) ALLOW Anywhere (v6)
443/tcp (v6) ALLOW Anywhere (v6)
3306/tcp (v6) DENY Anywhere (v6)
Anyone can look at that output and understand exactly what traffic your server accepts. I find that clarity is genuinely useful when I come back to a server six months later.
We’ll have more after this word from our partners.
Application Profiles
Some services ship with named profiles, which is a bit cleaner than remembering port numbers. To see what’s installed on your system:
sudo ufw app list
Available applications:
OpenSSH
On a fresh Ubuntu 24.04 install, you’ll usually just see OpenSSH. Install Nginx or Apache, and their profiles show up automatically. You can use a named profile like this:
sudo ufw allow "OpenSSH"
Rule added
Rule added (v6)
sudo ufw status
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)
Since FrankenPHP doesn’t ship with a UFW profile, the manual port rules we wrote earlier are the right approach. Named profiles are convenient when they exist, but they’re not required.
Rate Limiting
Your SSH port is open to the internet, which means bots are ALREADY trying to brute-force their way in. Check /var/log/auth.log if you don’t believe me. You can watch it happen in real time. It’s equal parts fascinating and horrifying.
Fortunately, UFW has this covered:
sudo ufw limit ssh
Rules updated
Rules updated (v6)
sudo ufw status
Status: active
To Action From
-- ------ ----
22/tcp LIMIT Anywhere
80/tcp ALLOW Anywhere
443/tcp ALLOW Anywhere
3306/tcp DENY Anywhere
22/tcp (v6) LIMIT Anywhere (v6)
80/tcp (v6) ALLOW Anywhere (v6)
443/tcp (v6) ALLOW Anywhere (v6)
3306/tcp (v6) DENY Anywhere (v6)
This updates your existing SSH allow rule with a rate-limited version. It blocks any IP that makes more than 6 connection attempts within 30 seconds. Legitimate users never hit that limit. Automated scripts get shut down fast.
Managing Your Rules
Over time, you’ll need to clean up rules as your server changes. To see rules with line numbers:
sudo ufw status numbered
sudo ufw status numbered
Status: active
To Action From
-- ------ ----
[ 1] 22/tcp LIMIT IN Anywhere
[ 2] 80/tcp ALLOW IN Anywhere
[ 3] 443/tcp ALLOW IN Anywhere
[ 4] 3306/tcp DENY IN Anywhere
[ 5] OpenSSH ALLOW IN Anywhere
[ 6] 22/tcp (v6) LIMIT IN Anywhere (v6)
[ 7] 80/tcp (v6) ALLOW IN Anywhere (v6)
[ 8] 443/tcp (v6) ALLOW IN Anywhere (v6)
[ 9] 3306/tcp (v6) DENY IN Anywhere (v6)
[10] OpenSSH (v6) ALLOW IN Anywhere (v6)
Delete by number:
sudo ufw delete 10
Deleting:
allow OpenSSH
Proceed with operation (y|n)? y
Rule deleted (v6)
And if you ever want to start completely fresh:
sudo ufw reset
Resetting all rules to installed defaults. This may disrupt existing ssh
connections. Proceed with operation (y|n)? y
Backing up 'user.rules' to '/etc/ufw/user.rules.20260413_141509'
Backing up 'before.rules' to '/etc/ufw/before.rules.20260413_141509'
Backing up 'after.rules' to '/etc/ufw/after.rules.20260413_141509'
Backing up 'user6.rules' to '/etc/ufw/user6.rules.20260413_141509'
Backing up 'before6.rules' to '/etc/ufw/before6.rules.20260413_141509'
Backing up 'after6.rules' to '/etc/ufw/after6.rules.20260413_141509'
That wipes every rule and disables UFW. If you reset, remember the golden rule: allow SSH before you enable it again.
It’s also important to notice that during our reset, UFW backups the rules just in case we didn’t mean to.
What You Need To Know
- UFW is a readable frontend for iptables. It makes firewall management something you can do in a few commands instead of a full afternoon.
- Allow SSH before enabling UFW. ALWAYS. No exceptions.
- Use
sudo ufw allowandsudo ufw denywith port numbers for services like FrankenPHP that don’t have named profiles. - Add
sudo ufw limit sshto throttle brute-force login attempts. - Use
sudo ufw status numberedto audit your rules andsudo ufw deleteto remove the ones you no longer need.



Leave a comment
Use the form below to leave a comment: