“I Just Got My First VPS — What Do I Do First?”
We hear this question almost every week. A new Canadian Web Hosting customer provisions their first Cloud VPS, logs in via SSH, and stares at a blank command line. They know they should lock it down, but where do you even start when the server is wide open to the internet?
The answer is always the same: install and enable a firewall. Specifically, UFW — the Uncomplicated Firewall. It ships pre-installed on Ubuntu, uses plain-English commands like ufw allow 80/tcp, and can turn a wide-open server into a locked-down fortress in under two minutes.
In this guide, we’ll walk through exactly how to set up UFW on your CWH Cloud VPS, configure rules for common services, harden it for production, and troubleshoot the things that go wrong. By the end, your server will drop everything except the traffic you explicitly allow.
What You’ll Need
Before we start, you’ll need:
- A Linux server — ideally Ubuntu 22.04 or 24.04 LTS (UFW is pre-installed). If you’re on Debian or another distro, you can install it via apt.
- Root or sudo access — all UFW commands require
sudo. - An active SSH session — you should be logged into your server now before we lock anything down.
For this guide, we’re using a CWH Cloud VPS running Ubuntu 24.04 LTS. Every CWH VPS comes with full root access, dedicated resources, and Canadian data centre hosting — the ideal environment for running your own infrastructure.
UFW itself has effectively zero system requirements. It runs as a Python CLI tool, and the iptables rules it generates live in the kernel with negligible memory overhead. Even a 1 vCPU, 512 MB RAM VPS handles UFW without any measurable performance impact.
Installing and Enabling UFW
On Ubuntu, UFW is usually pre-installed but disabled. Let’s check and set it up.
Step 1: Check if UFW is installed
sudo ufw --version
If you see output like ufw 0.36.2, you’re good. If not, install it:
sudo apt update
sudo apt install ufw -y
Step 2: Allow SSH — before enabling the firewall
This is the most important rule in this entire guide. Allow SSH before you enable UFW. If you enable the firewall first without an SSH rule, you’ll lock yourself out immediately.
sudo ufw allow 22/tcp
Better yet, add rate limiting while you’re at it:
sudo ufw limit 22/tcp
The limit command restricts connections to 6 attempts per 30 seconds from any single IP — a simple brute-force deterrent built right into UFW.
Step 3: Enable the firewall
sudo ufw --force enable
The --force flag skips the confirmation prompt. After enabling, UFW will automatically start on every future boot.
You should see:
Firewall is active and enabled on system startup
Step 4: Check the status
sudo ufw status verbose
Expected output:
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp LIMIT IN Anywhere
22/tcp (v6) LIMIT IN Anywhere (v6)
At this point, your server is already significantly more secure. Only SSH traffic (port 22) is allowed in. Everything else is silently dropped.
Essential Firewall Rules for Any Server
With the default deny-incoming policy active, you now need to explicitly open ports for whatever services your server runs. Here are the rules for common scenarios.
Web Server (HTTP/HTTPS)
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Or use the Nginx app profile if installed:
sudo ufw allow 'Nginx Full'
Custom Application Ports
sudo ufw allow 3000/tcp comment 'Node.js app'
sudo ufw allow 8080/tcp comment 'Alternative HTTP'
sudo ufw allow 9090/tcp comment 'Cockpit admin panel'
Database Access from Internal Networks Only
# MySQL — only allow from your office or VPN subnet
sudo ufw allow from 10.0.0.0/24 to any port 3306 proto tcp
# PostgreSQL
sudo ufw allow from 10.0.0.0/24 to any port 5432 proto tcp
Mail Server Ports
sudo ufw allow 25/tcp # SMTP inbound only; outbound is usually allowed by default
sudo ufw allow 587/tcp # Submission (authenticated SMTP)
sudo ufw allow 993/tcp # IMAPS
sudo ufw allow 995/tcp # POP3S
Block a Specific IP
sudo ufw deny from 192.0.2.100
To verify your full ruleset at any time:
sudo ufw status numbered
This shows each rule with a number, which you can use later for deletion or insertion.
Verifying Your Firewall Works
Don’t assume the rules are active — test them.
Test from outside the server
From another machine (or your local computer), try connecting to a port you didn’t open:
# From your local terminal:
nc -zv YOUR-SERVER-IP 3306
# Expected: Connection timed out — UFW is blocking it
nc -zv YOUR-SERVER-IP 22
# Expected: Connection succeeded — SSH is open
Check the firewall log
sudo tail -f /var/log/ufw.log
When you try to connect to a blocked port, you’ll see entries like:
[UFW BLOCK] IN=eth0 OUT= MAC=... SRC=203.0.113.50 DST=... PROTO=TCP DPT=3306
This confirms UFW is actively blocking the traffic. Each blocked packet is logged by default at the low logging level.
Verify rules are persistent
sudo ufw status verbose
All rules you add via the CLI survive reboots automatically — UFW saves them to /etc/ufw/user.rules and reloads them on startup.
Production Hardening
A basic UFW setup is good. These additions make it production-ready.
Rate-Limiting SSH (Already Done)
You set this up in Step 2:
sudo ufw limit 22/tcp
Behind the scenes, this creates iptables rules that track connection attempts per IP and drop traffic after 6 attempts in 30 seconds. Unlike Disable the limit — it’s a temporary throttle, not a permanent ban. For persistent blocking, pair UFW with Fail2ban or CrowdSec.
Allow Traffic by Source IP
For services that should only be accessible from specific locations:
# SSH only from your office
sudo ufw delete allow 22/tcp
sudo ufw allow from 203.0.113.50 to any port 22
# Admin panel only from your VPN subnet
sudo ufw allow from 10.100.0.0/16 to any port 9090
Interface-Specific Rules
If your server has multiple network interfaces (e.g., a public interface for the internet and a private interface for internal traffic):
# Allow SSH only on the management interface
sudo ufw allow in on eth1 to any port 22
# Deny all database access from the public interface
sudo ufw deny in on eth0 to any port 5432
# Allow everything on the private interface
sudo ufw allow in on eth1
Set Appropriate Logging Levels
# Default — blocked packets only (recommended for most servers)
sudo ufw logging low
# Medium — also logs allowed connections (useful during setup)
sudo ufw logging medium
# High/Full — use temporarily for debugging only (can fill disk)
sudo ufw logging high
On a busy web server, high logging can generate gigabytes of logs within hours. Stick with low in production and bump it up temporarily when debugging.
Defence in Depth with CWH’s Infrastructure
UFW is your host-based firewall — it runs on the server itself. For an additional layer of protection, CWH offers dedicated hardware firewalls (Juniper and Palo Alto appliances) that protect at the network edge. Together, they provide true defence in depth: the hardware firewall stops broad attacks at the perimeter, and UFW handles application-level rules on each server.
Troubleshooting Common UFW Issues
“I enabled UFW and now I’m locked out of SSH”
Symptom: You ran sudo ufw enable and your SSH session froze. You can’t reconnect.
Cause: You enabled UFW before adding an SSH allow rule, or you changed the default incoming policy to deny first.
Fix: Reboot the server from your CWH control panel (VPS > Reboot in Rescue Mode or boot into recovery). Once in rescue, mount the filesystem and disable UFW:
sudo ufw disable
exit
reboot
Then log back in over SSH and follow the correct order: allow SSH first, then enable UFW.
Prevention: Always run sudo ufw allow 22/tcp before sudo ufw enable. Some admins create a cron job that disables UFW after a failed SSH check, but the simpler approach is just getting the order right.
“Docker containers bypass my UFW rules”
Symptom: You set ufw deny 3306, but your MySQL container on port 3306 is still accessible from outside.
Cause: Docker manipulates iptables directly, inserting its own rules before UFW’s rules in the FORWARD chain. This means Docker’s port publishing bypasses UFW entirely.
Fix: Three approaches, in order of recommendation:
- Publish only to localhost:
docker run -p 127.0.0.1:3306:3306 ...— then use a reverse proxy (Nginx, Caddy) to route public traffic through the UFW-controlled host. - Disable Docker’s iptables management: Add
"iptables": falseto/etc/docker/daemon.json, thensystemctl restart docker. Note: this breaks Docker’s built-in container networking — only do this if you understand the implications. - Use the
ufw-dockertool: A community script that manages Docker-published ports through UFW. See the ufw-docker GitHub project.
“I deleted the wrong rule by number”
Symptom: You ran sudo ufw delete 4 but removed the wrong rule, and now SSH or your web server is down.
Cause: ufw status numbered shows rules in a specific order. Deleting by number removes it permanently unless you know the exact state.
Fix: Re-add the rule manually. If you don’t remember the exact rule, check /etc/ufw/user.rules — it stores the current ruleset (though editing it directly is not recommended). For a clean slate:
sudo ufw --force reset
# Then re-add your rules from scratch
Prevention: Use ufw status numbered to double-check the rule number before deleting. Alternatively, delete by rule specification instead of number:
# Safer: delete by specifying the exact rule
sudo ufw delete deny 25/tcp
“UFW rules disappear after reboot”
Symptom: Your custom rules are gone after a server restart. ufw status shows only default rules.
Cause: You added rules in /etc/ufw/before.rules or after.rules but didn’t run ufw reload before rebooting — the file was saved but UFW never loaded the new version into its active ruleset.
Fix: After editing any file in /etc/ufw/, always run:
sudo ufw reload
Then verify the rules are active before rebooting:
sudo ufw status verbose
What’s Next — Beyond the Basics
UFW is the first — and most impactful — security step for any Linux server. With a default-deny policy and a handful of allow rules, you’ve gone from wide-open to locked-down in under five minutes. Here’s what to tackle next:
- Pair UFW with Fail2ban —
ufw limitthrottles brute force attempts, but it doesn’t ban persistently. Fail2ban creates lasting bans after repeated failures. See our Fail2ban vs CrowdSec comparison to choose your tool. - Harden SSH access — UFW blocks unwanted network traffic, but SSH itself has configuration weaknesses. Our SSH Hardening Guide covers key-based auth, disabling root login, and moving to a non-default port.
- Build a complete security baseline — UFW is one component of a layered defence. Our 9 Essential Security Tools for Your VPS roundup covers fail2ban, CrowdSec, ClamAV, Wazuh, and more — all the tools you need for a production-ready server.
- Let CWH manage it — If you’d rather not think about firewall rules yourself, CWH’s Managed Security plans include firewall configuration, OS hardening, and 24/7 monitoring — everything we covered in this guide, handled by our team.
Every customer who asks us “what do I do first?” gets the same answer: install UFW, allow SSH, enable the firewall, document what you opened. It takes two minutes and stops 99% of automated scanning and brute force traffic hitting your server.
Start with the firewall. Then build from there.
Sources and Further Reading
- Ubuntu Community Help: UFW — command reference and common firewall examples.
- Ubuntu manpage: ufw(8) — syntax for rules, delete operations, logging, reload, and dry-run checks.
- SSH port basics — why SSH exposure matters and why network-level restrictions help.
Be First to Comment