“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:

  1. 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.
  2. Disable Docker’s iptables management: Add "iptables": false to /etc/docker/daemon.json, then systemctl restart docker. Note: this breaks Docker’s built-in container networking — only do this if you understand the implications.
  3. Use the ufw-docker tool: 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 Fail2banufw limit throttles 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