The hook: Most UFW tutorials stop at ufw allow 22 and call it done. That’s a firewall with one rule and the entire internet on the other side of it. Here’s how to configure UFW so your homelab services are only reachable from your own network, SSH is rate-limited instead of wide open, and you don’t lock yourself out at 11pm on a Tuesday.
How to Configure UFW for a Homelab (Short Answer)
Set UFW to default-deny incoming and default-allow outgoing, then rate-limit SSH with sudo ufw limit ssh before you enable it. For homelab services, don’t just open the port to the world: restrict it to your LAN with sudo ufw allow from 192.168.1.0/24 to any port 8096 proto tcp, swapping in your subnet and the service’s port. Verify everything with sudo ufw status verbose before you trust it.
That single line, restricting a rule to a source subnet instead of “anywhere,” is the part most Ubuntu firewall tutorials skip. Generic tutorials are written for a single-purpose server: one box, one service, one public IP. A homelab is a Proxmox host running a dozen VMs, a Docker host running Jellyfin and Vaultwarden, and probably a Pi doing DNS, most of which have no business being reachable from outside your house.
This is the “firewall basics” step our full homelab network security guide lists as one of its seven zero-trust steps. A default-deny UFW policy alone only tells you what’s blocked from the internet; it says nothing about what’s still wide open to anyone on your Wi-Fi. Scoping rules to a source subnet closes that gap, and this guide covers the full setup plus the gotcha that catches almost everyone running Docker behind UFW.
Career context: UFW is a friendly wrapper around Netfilter, the same packet-filtering framework behind raw iptables and its modern replacement, nftables. Default-deny inbound with explicit allow rules scoped by source and port is identical logic to how AWS Security Groups and Azure NSGs are configured. Network Engineer and Cloud Security roles asking for “firewall management experience” (£35-55k junior, £60k+ with cloud platform experience) are asking for exactly this reasoning, on a bigger box. Scoping a rule to your LAN subnet at home is the same skill as scoping a Security Group to a VPC CIDR block in production.
Prerequisites
- An Ubuntu server (this guide is written against Ubuntu 24.04 LTS but applies to any recent Ubuntu or Debian release)
- SSH access, ideally already configured and working before you touch the firewall
- sudo privileges
- Your LAN subnet written down. Run
ip addr showand find the CIDR next to your main interface (commonly something like192.168.1.0/24or10.0.0.0/24). You’ll use this constantly in this guide.
Before you enable UFW on a remote box, confirm you have a way back in if you get locked out. Console access via your hypervisor (Proxmox, ESXi), IPMI/iDRAC, or physical access is your safety net.
Step 1: Check What You’re Working With
UFW ships installed by default on Ubuntu Desktop and Server. Confirm it’s there and check its current state before changing anything:
sudo ufw status verbose
Status: inactive
If it’s not installed for some reason:
sudo apt update
sudo apt install ufw
Do not run ufw enable yet. The default deny policy applies the instant you enable it, and if SSH isn’t allowed first, your session drops and doesn’t come back.
Step 2: Set the Default-Deny Posture
UFW’s two defaults are the whole philosophy in two lines: deny everything coming in, allow everything going out.
sudo ufw default deny incoming
sudo ufw default allow outgoing
These are defaults, not active rules yet, they take effect once UFW is enabled. Every service you want reachable from now on needs an explicit allow rule. That’s the point: nothing gets in by accident.
Step 3: Allow and Rate-Limit SSH Before You Enable Anything
Enable UFW without an SSH rule and you cut your own session. This is the step that trips up almost everyone the first time.
Don’t just allow SSH, rate-limit it. ufw limit denies an IP address that attempts six or more connections within 30 seconds, which blunts brute-force scanning without touching legitimate use:
sudo ufw limit ssh
# or, equivalently, by port:
sudo ufw limit 22/tcp
If you’ve moved SSH off port 22 (a reasonable move covered in our SSH setup guide), rate-limit that port number instead. Now enable UFW:
sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup
Confirm SSH still works by opening a second terminal and connecting fresh, without closing your current session. If the new connection fails, you still have your original session open to fix the rule. This is the entire safety net: never close the session you’re troubleshooting from until a second connection confirms the fix worked.
ufw limit is rate-limiting, not authentication. It slows brute-force attempts but doesn’t replace key-based authentication or fail2ban. For actively banning repeat offenders rather than just rate-limiting them, see our fail2ban setup guide, which pairs directly with this rule.
Step 4: The Homelab Move: Restrict Services to Your LAN Subnet
This is the part generic “how to install UFW” tutorials from the big hosting providers almost never cover, because they’re written for public-facing VPS boxes, not homelabs with a trusted local network.
The naive approach to exposing a homelab service is sudo ufw allow 8096/tcp for Jellyfin. That opens port 8096 to anyone on the internet who finds your IP address, an unnecessary attack surface for zero benefit if you’re not intentionally exposing that service publicly.
Instead, scope the rule to your LAN subnet using UFW’s from clause:
# Jellyfin, LAN only
sudo ufw allow from 192.168.1.0/24 to any port 8096 proto tcp comment 'Jellyfin - LAN only'
# Proxmox web UI, LAN only
sudo ufw allow from 192.168.1.0/24 to any port 8006 proto tcp comment 'Proxmox UI - LAN only'
# Portainer, LAN only
sudo ufw allow from 192.168.1.0/24 to any port 9000 proto tcp comment 'Portainer - LAN only'
# Vaultwarden, LAN only
sudo ufw allow from 192.168.1.0/24 to any port 8080 proto tcp comment 'Vaultwarden - LAN only'
Replace 192.168.1.0/24 with your actual subnet from the prerequisites step. A scan against your public IP for port 8096 now finds nothing, while a device on your own network reaches Jellyfin normally. That’s a genuine reduction in attack surface, not obscurity: the service simply doesn’t respond to traffic that didn’t originate from inside your network. The optional comment field pays off later, when you’re auditing rules and “Jellyfin – LAN only” tells you more than a bare port number.
Scope to a single trusted host instead of the whole subnet for anything genuinely sensitive: sudo ufw allow from 192.168.1.50 to any port 8006 proto tcp allows only that one workstation to reach the Proxmox UI.
Step 5: Use App Profiles Instead of Memorising Ports
UFW supports named “application profiles” that map a friendly name to one or more ports. See what’s already registered:
sudo ufw app list
Available applications:
Nginx Full
Nginx HTTP
Nginx HTTPS
OpenSSH
Packages that install a UFW profile drop a definition into /etc/ufw/applications.d/. Allow by name instead of remembering the port:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
For services without a built-in profile, write your own. Create /etc/ufw/applications.d/jellyfin:
[Jellyfin]
title=Jellyfin Media Server
description=Jellyfin web UI and streaming port
ports=8096/tcp
Register it and allow it, scoped to your LAN as before:
sudo ufw app update Jellyfin
sudo ufw allow from 192.168.1.0/24 to any app Jellyfin
Not just tidiness: when future you reads ufw status in six months, “Jellyfin” is self-documenting in a way “8096/tcp” is not.
Step 6: Turn On Logging
A firewall with no logging only tells you it blocked something if you go looking. Turn it on:
sudo ufw logging on
UFW logs to /var/log/ufw.log via rsyslog. Tail it to watch rules fire in real time:
sudo tail -f /var/log/ufw.log
Logging levels range from low to full. low is a reasonable default, logging blocked packets without noise from every allowed connection; use sudo ufw logging medium for more detail while debugging.
Step 7: Verify, Audit, and Clean Up
Trust nothing you haven’t checked. ufw status verbose shows the full policy plus every active rule:
sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp (OpenSSH) LIMIT IN Anywhere
8096/tcp ALLOW IN 192.168.1.0/24
8006/tcp ALLOW IN 192.168.1.0/24
For numbered output you can reference when deleting a rule, use ufw status numbered:
sudo ufw status numbered
sudo ufw delete 3
Do this periodically, not just on setup day. Homelabs accumulate rules the way garages accumulate cables: a service gets decommissioned and its firewall rule quietly outlives it.
The Docker Gotcha Nobody’s UFW Tutorial Mentions
If you run Docker, your UFW rules may not be doing what you think. Docker manipulates iptables directly and inserts its own rules into the DOCKER-USER chain, consulted before UFW’s own filtering for forwarded traffic. In practice, docker run -p 8080:80 ... can publish that container to the entire internet even though UFW’s default policy is deny and you never wrote an allow rule for port 8080.
The lighter fix: bind published ports to a specific address instead of all interfaces:
# Only reachable from localhost (put it behind a reverse proxy instead)
docker run -p 127.0.0.1:8080:80 ...
# Only reachable from your LAN interface's own address
docker run -p 192.168.1.10:8080:80 ...
The more thorough fix is adding explicit rules to the DOCKER-USER chain, Docker’s own documented integration point for firewall tools like UFW (community scripts like ufw-docker automate this). Don’t assume UFW protects Docker-published ports until you’ve tested from outside your network.
UFW Is One Layer, Not the Whole Stack
A well-configured UFW ruleset is a genuinely strong layer, but it’s still only one layer. The full homelab network security guide covers the wider posture: an overlay VPN instead of port-forwarding on your router, containers treated as untrusted by default, 3-2-1 backups, and monitoring that actually alerts you. If you need to reach a homelab service from outside your network, the better answer is a VPN back into your LAN, or a service like Cloudflare Tunnels that exposes it without opening any inbound port at all. UFW’s job is making sure that if a device on your own network gets compromised, it still can’t reach services it has no business touching.
Troubleshooting
I locked myself out over SSH
With console access (Proxmox, IPMI, physical keyboard), log in locally and run sudo ufw allow OpenSSH or sudo ufw disable, then fix the rule before re-enabling. With no console access and no second session open when you ran ufw enable, you’re locked out until you get physical or out-of-band access, exactly why that second-session check isn’t optional. See our common SSH errors guide if the connection fails for an unrelated reason.
A rule doesn’t seem to be doing anything
Check ordering with sudo ufw status numbered: UFW stops at the first matching rule, so a broad allow above a narrower deny will shadow it. Also confirm where you’re testing from; a “LAN only” rule always succeeds from inside the LAN, so test from outside (mobile data with Wi-Fi off works) to confirm it’s actually blocked.
UFW says active but the service is still reachable from the internet
Usually one of three things: Docker’s own iptables rules are handling the port (see above), your router still has a port-forward rule sending traffic straight to the host, or the service is listening on an interface UFW doesn’t expect. Check sudo ss -tulnp to confirm what’s listening where.
Rules disappeared after a reboot
Confirm UFW is enabled to start on boot with sudo systemctl status ufw.
Lessons Learned
- Always rate-limit or allow SSH, and confirm it in a second session, before running
ufw enable. It’s the single most common way people lock themselves out. - A bare
ufw allow <port>opens that port to the entire internet. Scope homelab-only services withfrom <subnet>instead. - UFW rules do not automatically apply to Docker-published ports. Test from outside your network before trusting a container is restricted.
- UFW is one layer. Pair it with an overlay VPN or tunnel for remote access, and with fail2ban for active banning rather than just rate-limiting.
Career Application
Firewall administration is one of the most transferable skills in this field, because the underlying logic (default-deny, explicit allow, scope by source, log everything) is identical whether you’re looking at a UFW ruleset, an AWS Security Group, or an Azure NSG. The tool changes; the reasoning doesn’t.
Interview Talking Points
- “How do you approach firewall configuration?” Default-deny inbound, explicit allow rules scoped to the narrowest source that still works, logging on so you can audit reality rather than assumptions.
- “What’s the difference between UFW and iptables?” UFW is a policy-management frontend for the same Netfilter framework iptables and nftables use, a stepping stone to raw rule-writing that enterprise environments still rely on heavily.
Portfolio idea: document your full UFW ruleset (with comments) as an infrastructure-as-code artefact, paired with an nmap scan of your homelab from outside the network, before and after LAN-scoping. That diff is the portfolio piece, and it compares directly against the Ansible-managed baseline in our Ansible security baseline guide. CompTIA Network+ and Security+ both cover firewall fundamentals directly; AWS/Azure associate certs apply the same logic to Security Groups and NSGs.
Next Steps
- Pair this with fail2ban. UFW rate-limits; fail2ban actively bans repeat offenders. See the fail2ban SSH guide.
- Read the full homelab security posture. UFW is one of seven steps in the homelab network security guide.
- Stop forwarding ports on your router. Set up Cloudflare Tunnels for anything that genuinely needs to be public.
- Test your Docker host from outside your network rather than assuming UFW is protecting published container ports.
Resources
- man ufw (Ubuntu manpages)
- Ubuntu Community Help: UFW
- Debian Wiki: Uncomplicated Firewall
- Docker Docs: Packet Filtering and Firewalls
Frequently Asked Questions
Curated for schema (_rtm_schema_faq), not machine-extracted from headings above.
Does UFW replace iptables?
No. UFW is a simplified frontend that writes rules into the same Netfilter framework iptables and nftables use. It doesn’t replace the packet-filtering engine, it just makes common cases easier to configure correctly.
How do I check if UFW is actually blocking traffic, not just showing “active”?
Run sudo ufw status verbose, then test from outside your own network (mobile data with Wi-Fi off works) by trying to reach a port you expect to be blocked. “Active” only confirms UFW is running, not that a rule behaves the way you assume.
Will UFW block ports published by Docker containers?
Not by default. Docker inserts its own iptables rules, evaluated before UFW’s, so a container published with docker run -p 8080:80 can be reachable from the internet regardless of your UFW policy. Bind published ports to a specific interface, or add explicit rules to the DOCKER-USER chain, to bring them under UFW’s control.
What’s the difference between ufw allow ssh and ufw limit ssh?
allow permits unlimited connection attempts. limit automatically denies an IP address that attempts six or more connections within 30 seconds, blunting brute-force scanning. It’s not a replacement for key-based authentication or fail2ban, just a first layer of friction.
Can I restrict a homelab service to only my local network with UFW?
Yes, this is the single highest-value UFW technique for a homelab. Use sudo ufw allow from <your-subnet> to any port <port> proto tcp, for example sudo ufw allow from 192.168.1.0/24 to any port 8096 proto tcp for Jellyfin. The service simply won’t respond to traffic from outside your subnet, rather than relying on a login screen alone to keep outsiders out.
Related on ReadTheManual
- The 7 Zero-Trust Steps for Your Homelab
- Identity Is the New Castle Walls: Machine Identity Governance

ReadTheManual is run, written and curated by Eric Lonsdale.
Eric has over 20 years of professional experience in IT infrastructure, cloud architecture, and cybersecurity, but started with PCs long before that.
He built his first machine from parts bought off tables at the local college campus, hoping they worked. He learned on BBC Micros and Atari units in the early 90s, and has built almost every PC he’s used between 1995 and now.
From helpdesk to infrastructure architect, Eric has worked across enterprise datacentres, Azure environments, and security operations. He’s managed teams, trained engineers, and spent two decades solving the problems this site teaches you to solve.
ReadTheManual exists because Eric believes the best way to learn IT is to build things, break things, and actually read the manual. Every guide on this site runs on infrastructure he owns and maintains.
Enjoyed this guide?
New articles on Linux, homelab, cloud, and automation every 2 days. No spam, unsubscribe anytime.




