→ From Coder to Shipper
From Coder to Shipper · Infrastructure

ufw — your VPS's gatekeeper

Your VPS has a public IP and, by default, every port is open. ufw is the easy, safe way to close the doors you don't use — without ever touching iptables.

← / → to navigate · EN/VI switch below · the operator's view — mental model first, a handful of small examples

The problem

A fresh VPS is open to the whole internet

The moment you buy it, your machine has a public IP — and there is no firewall in front of it by default.

A public IP

Your VPS has an address like 203.0.113.10. Anyone can send packets to any port on it — and the bots know every address that exists.

Every port is open by default

A default VPS has no firewall: SSH :22, MySQL :3306, Postgres :5432, Redis :6379 — if a service is listening, the whole world can reach it.

Scanners never sleep

Bots scan the entire internet for open ports around the clock. An exposed Redis or database is found within hours — and the bots already carry the exploits.

You need a door with a lock

Your website should stay reachable — your databases and SSH should not. Something has to stand in front and decide, per port, who may come in.

The picture

Today: every door stands open

No firewall yet — every service answers anyone who knocks.

anyone
The internet — everyoneusers + scanners + botnets
→over the public internet
VPS — 203.0.113.10 no firewall — all ports open
SSH:22
Web (Nginx):80 / :443
MySQL:3306
Postgres:5432
Redis:6379
everything else:1024 …

Same machine you'll use to ship your app — except right now, a stranger from anywhere can talk to MySQL directly. That's the problem this deck fixes.

A good firewall is invisible: set it up once, open only the doors you need, and never think about it again — until something unexpected shows up in the log.
— the operator's motto
01
Part 1

What ufw actually is

One tool, three properties: easy, safe by default, and stateful. Get these and every command becomes obvious.

The tool

ufw in one breath

The easy front-end

ufw (Uncomplicated Firewall) is a friendly command-line front-end to the real packet filters — iptables and nftables. It translates your one-line rules into what the kernel actually executes. You never need to touch the raw filter language.

Safe defaults

The moment you run ufw enable, the posture flips: deny everything coming in, allow everything going out. From then on, you only open the specific doors you need.

Stateful

It tracks connections: if your machine started the conversation, the reply is allowed automatically. That's what makes 'deny inbound' safe — in a minute we'll see why.

Already installed

On a typical Ubuntu VPS, ufw is preinstalled and just sitting there, inactive — waiting for you to turn it on. One command later, your VPS stops answering strangers.

The default posture

Deny in, allow out — one switch

Incoming: DENY

After ufw enable, every connection coming from outside is refused — unless you added an explicit allow rule for that port. The default answer to strangers is 'no'.

That's the whole security model in one sentence: nothing gets in unless you opened that door yourself.

Outgoing: ALLOW

Your VPS still talks to the outside world freely — apt updates, DNS lookups, your app calling an external API or database. Outbound stays open by default.

So your site keeps working — while the doors the world can walk through stay locked.

The detail that makes it click

Why 'deny inbound' doesn't break your own traffic

You click, your browser asks for a page. Two directions of traffic — ufw treats them differently, for a good reason.

out
Your browseryou start the request
→request goes OUT — allow outgoing
ufwnew connection, started from inside → ALLOW
→
The website / API
in (reply)
The website / APIsends the page back
←reply comes IN — belongs to YOUR connection
ufwreturn traffic, tracked connection → ALLOW
←
Your browser

The gatekeeper keeps a notebook of who it let out. Anything coming back on one of those conversations passes without a second question. A stranger who knocks first still gets a 'no'.

Plain language

Stateful, without the jargon

The notebook

The kernel keeps a table of active connections: 'laptop → 203.0.113.10:443, started 5 seconds ago, still talking'. Every packet is checked against that table first, not against a blind list of rules.

The doorbell rule

Think of ufw as a building with a strict reception: nobody gets in unless you invited them. But if you went out to buy coffee, the barista can walk you right back in — your card is already on the guest list.

Why it matters for you

Your app calls external APIs, apt pulls updates, browsers fetch your pages. Statefulness is what lets all of that keep working while 'deny inbound' holds the line against strangers.

02
Part 2

The core commands

Five verbs cover 95% of daily firewall work: enable, status, allow, deny, delete — plus one line for the global defaults.

The switch and the mirror

Turn it on, then look at it

sudo ufw enable

Turns the firewall on — immediately, and at every boot. Until this runs, ufw is a notebook with no gatekeeper: your rules exist, but nothing is actually filtered.

ufw status

Shows whether ufw is active and lists every rule in effect, with its action (ALLOW / DENY) and direction. This is the first thing you type after every change.

ufw status verbose

The same view, plus the default incoming/outgoing policy and whether logging is on. Good for the quick 'is everything the way I think it is?' check.

In the terminal

Turning the gatekeeper on

Note the warning — that's ufw being honest with you about SSH (the golden rule comes later).

ops@vps — zsh
$ ufw status
Status: inactive
$ sudo ufw enable
Command may disrupt existing ssh connections.
Proceed with operation (y|n)? y
Firewall is active and enabled on system startup
$ ufw status
Status: active
The daily drivers

allow, deny, delete — open, close, undo

ufw allow <port>/tcp

Opens a door: traffic on that port may come in from anywhere. Works with a port number (22/tcp, 8080/tcp) or a service name (OpenSSH, 'Nginx Full'). Add /udp for services that speak UDP.

ufw deny <port>

Closes a door explicitly. Handy when the default already denies — it documents your intent, and gives you a hook for logging the noise or allowing one specific IP later.

ufw delete <rule>

Removes a rule you added earlier: ufw delete allow 3306/tcp. Delete doesn't 'deny' — it puts the port back to whatever the default policy says.

Each rule is one line

Every rule is a single line in ufw's notebook. That's the whole data model — no config files to hunt for, no restart needed. The notebook is saved on disk and reloaded at boot.

A tiny sketch

The commands you'll actually type

bash
# turn it on (now + at every boot)
sudo ufw enable

# what's actually active?
ufw status

# the daily drivers
ufw allow 22/tcp          # SSH, by port number
ufw allow OpenSSH         # same rule, by service name
ufw allow 'Nginx Full'    # 80 + 443 in one go
ufw deny 3306/tcp         # MySQL: no outside traffic
ufw delete deny 3306/tcp  # undo that rule

Everything here is one line, reversible, and shows up instantly in ufw status. No config file, no restart.

The showcase

A typical web-VPS rule set

SSH plus the two web ports. That's the entire firewall for most small projects — nothing else open.

ops@vps — zsh
# the whole web-VPS rule set, in two commands
$ sudo ufw allow OpenSSH
Rule added
$ sudo ufw allow 'Nginx Full'
Rules updated
$ ufw status
Status: active
To Action From
-- ------ ----
22/tcp ALLOW Anywhere
80/tcp ALLOW Anywhere
443/tcp ALLOW Anywhere
The global switch

Setting the defaults for everything else

ufw default deny incoming

The global 'strangers get a no'. This is the policy that makes everything not explicitly allowed unreachable. It's already the default after enable — but stating it out loud is good hygiene.

ufw default allow outgoing

The global 'your VPS can reach the world'. Outbound stays open so updates, DNS lookups and your app's API calls keep working.

Defaults cover the long tail

Think of it as the policy for every port you forgot about: 3306, 5432, 6379, 10000… Anything without an explicit allow inherits 'deny incoming'. That long tail is exactly what scanners are after.

03
Part 3

The mental model

How to think about the whole thing in one sentence — and the one mistake that costs you an evening.

The picture

The gatekeeper in front

Three doors open for the people who need them; everything else gets a 'no' before it ever reaches a service.

the internet
Internet — users + scannerseveryone, all the time
→every packet
ufw — the gatekeeper default: deny incoming · allow outgoing
22/tcpALLOW
80/tcpALLOW
443/tcpALLOW
3306/tcpDENY
5432/tcpDENY
the restDENY
→only allowed traffic
VPS servicesNginx :80/:443 · SSH :22
MySQL :3306 — hidden

The scanner keeps knocking on 3306 and 5432 — and gets nothing. Your users get your website, and only you get the shell.

The golden rule

Never lock yourself out of SSH

Firewall changes are dangerous: a wrong deny or delete can cut your own SSH — and there is no 'undo' from outside.

SSH is your only key

You manage the VPS through SSH. If a rule blocks :22, you can't get in — not even to fix it. The fix then happens through your provider's web console (VNC/rescue), which is slow and unpleasant.

Open SSH first, always

Before ufw enable — or any deny/delete — make sure an allow rule for your SSH port exists. If your SSH runs on a non-standard port, that's the one to allow.

Verify after every change

Open a SECOND terminal before you tighten things, and test that you can still connect. If the new session dies, the old one is still in — you get 30 seconds to delete the bad rule.

The safe order

Tighten without getting locked out

Four moves, in this order, every single time.

  1. 1

    Allow your SSH port first

    ufw allow 22/tcp (or whatever port your SSH listens on). This rule must exist before anything else gets stricter.

  2. 2

    Turn the firewall on

    sudo ufw enable — the default posture kicks in: deny incoming, allow outgoing. Your SSH rule keeps you in.

  3. 3

    Open a second terminal, verify

    From your machine, open another SSH session. It connects? You're safe to continue. It doesn't? Your first session is still alive — fix the rule from there.

  4. 4

    Then tighten, and check status

    Add the allows for your real services, deny what should be closed, and read ufw status to confirm the final state matches your intent — every time.

The paper trail

Logging — see who's knocking

ufw logging on

Starts recording every dropped or rejected packet: source IP, destination port, protocol. It's forensic evidence of who's probing your box and when.

Where it lands

Entries go to the kernel log — /var/log/kern.log — lines like 'IN=eth0 ... PROTO=TCP DPT=3306 ... REJECT'. tail -f it and watch the scanners work.

On by default, cheap to leave on

ufw logging on is the default after enable, and the overhead on a small VPS is negligible. The day a port gets hammered, you'll be glad the notebook was open.

Where ufw sits

ufw vs iptables/nftables — one line each

iptables / nftables

The real machinery in the kernel: chains, tables, raw rules. Extremely powerful, extremely verbose — and one typo away from blocking yourself out.

ufw

A friendly one-line front-end that writes the same kernel rules for you, keeps sane defaults, and can undo them just as easily. For 95% of VPS work, you stop at ufw.

ufw is the gatekeeper in front of your services: open only the doors you need, and let the rest of the internet knock on a locked wall.
— that's the whole deck

Open only the doors you need

ufw enable → allow your SSH → verify from a second terminal → allow 80/443 → the rest stays closed. Ship the app; the gatekeeper's got the door.