← Back to blog

How to Set Up a Firewall on Your VPS: A Beginner's Guide

Maria Ilinca Bostan

A firewall on a VPS does one job: it drops every inbound connection except the handful you deliberately allow. On Ubuntu and Debian the fastest correct route is ufw; on AlmaLinux and Rocky it is firewalld. Both are front-ends to nftables. This guide gets a working, locked-down ruleset in about ten minutes — and shows you how not to lock yourself out doing it.

Before the first rule: do not lock yourself out

The single most common firewall accident is enabling a default-deny policy without first allowing SSH. The connection you are typing into dies mid-command and the machine is unreachable.

Two protections, both worth the thirty seconds:

# 1. a dead-man switch — disables the firewall in 10 minutes unless you cancel
sudo bash -c 'echo "ufw --force disable" | at now + 10 minutes' 2>/dev/null || \
  sudo bash -c '(sleep 600 && ufw --force disable) &'

# 2. a second SSH session, already open, that you do not touch

Once the rules are proven, cancel the timer with sudo atrm $(atq | cut -f1) or kill the background job. If you do get locked out, a VPS with console access recovers in a minute — but plan not to need it.

Ubuntu and Debian: ufw

sudo apt update && sudo apt install -y ufw

# default posture: nothing in, everything out
sudo ufw default deny incoming
sudo ufw default allow outgoing

# SSH FIRST — before enabling
sudo ufw limit 22/tcp comment 'ssh, rate limited'

# web
sudo ufw allow 80/tcp  comment 'http'
sudo ufw allow 443/tcp comment 'https'

sudo ufw enable
sudo ufw status verbose

ufw limit rather than ufw allow for SSH is the detail worth knowing: it blocks a source IP that opens more than six connections in thirty seconds, which removes the bulk of automated brute-force noise without any extra software.

Restricting a port to one address

# database reachable only from your application server
sudo ufw allow from 203.0.113.10 to any port 5432 proto tcp

# SSH only from your office range
sudo ufw delete limit 22/tcp
sudo ufw allow from 203.0.113.0/24 to any port 22 proto tcp

Do not restrict SSH to a single IP unless that IP is genuinely static. A dynamic home address that changes overnight is the second most common way people lock themselves out.

AlmaLinux, Rocky and CentOS: firewalld

sudo dnf install -y firewalld
sudo systemctl enable --now firewalld

sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-all

firewalld's --permanent flag writes the rule to disk but does not apply it until --reload. A rule added without --permanent works immediately and vanishes at reboot. Getting these the wrong way round produces a firewall that is correct right up until the machine restarts.

# restrict a port to one source with a rich rule
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" \
  source address="203.0.113.10" port port="5432" protocol="tcp" accept'
sudo firewall-cmd --reload

IPv6 is not optional

If your VPS has an IPv6 address — and most do — a firewall covering only IPv4 leaves every service reachable over v6. ufw handles both when IPV6=yes is set in /etc/default/ufw (the default on current releases). Verify rather than assume:

sudo ufw status verbose | grep -i v6
sudo ip6tables -L -n | head

The same applies to any application-level allowlist you write. An SSH AllowUsers restriction by IPv4 address does nothing to a connection arriving over IPv6.

Close the ports the firewall cannot help with

A firewall protects services you forgot were listening. It is better not to have them listening at all. Check what is bound to a public address:

sudo ss -lntup

Anything showing 0.0.0.0: or [::]: is publicly reachable. Databases, Redis, Memcached, Elasticsearch and admin panels should bind to 127.0.0.1 instead. A Redis bound to all interfaces with no password is compromised within hours of being visible — the firewall is your second line of defence, not your first.

Add fail2ban for the noise the firewall cannot judge

A firewall cannot tell a failed password from a successful one. fail2ban reads the logs and bans sources that repeatedly fail.

sudo apt install -y fail2ban
sudo tee /etc/fail2ban/jail.local >/dev/null <<'EOF'
[DEFAULT]
bantime  = 1h
findtime = 10m
maxretry = 5
backend  = systemd

[sshd]
enabled = true
EOF
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd

The larger win is turning password authentication off entirely. In /etc/ssh/sshd_config: PasswordAuthentication no, PermitRootLogin prohibit-password, then sudo systemctl reload sshd. Confirm your key works in a second session before you close the first.

What a host firewall does not do

Worth being clear about the boundary. A host firewall drops unwanted connections at your server. It does not stop a volumetric DDoS attack — by the time the packets reach your VPS, they have already consumed the bandwidth that matters, and dropping them costs you CPU. That is why network-level scrubbing sits in front of the machine; our DDoS protection is applied upstream on every plan, and our guide to DDoS attacks covers what each layer can and cannot absorb.

It also does not protect you from an application vulnerability on a port you deliberately opened. Port 443 being open is the point; what listens behind it is your responsibility.

A working baseline

PortRuleNote
22limit or restrict by sourceKey-only auth; consider a non-standard port to cut log noise
80 / 443allowOnly if the machine serves web traffic
3306 / 5432allow from application IP onlyBetter: bind to localhost and do not open at all
6379 (Redis)never openBind to 127.0.0.1, set a password
Everything elsedenyDefault policy handles it

Frequently asked questions

ufw, firewalld, iptables or nftables — which should I use?

nftables is the kernel subsystem; iptables is the legacy interface to it; ufw and firewalld are friendlier front-ends. Use the front-end your distribution ships with. Mixing two of them on one machine produces rules that appear to exist but do not apply.

Do I need a firewall if my provider offers one?

A network firewall and a host firewall protect against different mistakes. Run both — the host firewall is the one that survives you moving providers.

Will a firewall slow my server down?

Not measurably at the rule counts described here. nftables evaluates a few dozen rules in microseconds.

How do I test the rules from outside?

From another machine: nmap -Pn -p 1-1024 your.server.ip. Testing from the server itself tells you nothing, because loopback traffic bypasses the rules you care about.

Next steps

A firewall is step one of hardening, not the whole of it. Continue with server security best practices for SSH keys, updates and least-privilege, and set up automated backups so a bad day is recoverable. Every command here runs unchanged on a voxa.host KVM VPS with full root access — and at €0.0056/hr, practising the whole sequence on a throwaway instance costs a few cents.

Ready in under a minute

Deploy your first server now.

No contracts, no minimums. Start on an Ion KVM VPS at €0.0063 an hour and move to a monthly bare-metal server the day you outgrow it.

$ voxa deploy --plan ion --location amsterdam

KVM VPS billed hourly, capped monthly · Dedicated billed monthly · No setup fee

Included on every plan
Free IPv4 + IPv6
Every VPS
Unmetered traffic
1–10 Gbps
DDoS mitigation
2.5 Tbps
Root / IPMI access
Included
Setup fee
€0.00
Minimum term
None