Firewall Configuration and Port Forwarding Best Practices for Homelabs
We earn commissions when you shop through the links on this page, at no additional cost to you. Learn more.
Port forwarding is how I expose my self-hosted services to the internet, but it's also the fastest way to turn your homelab into a liability. I've learned this the hard way—and I've also learned how to do it right. In this article, I'll walk you through stateful firewalls, UFW rules that actually protect you, the hidden dangers of UPnP, and when a VPS reverse proxy is worth every penny.
Why Firewalls Matter in Homelabs
Most homelabs live behind a consumer router that handles basic NAT and firewall duties. But that router is often a black box: you don't know what ports are really open, what traffic is being logged, or whether that default password was ever changed. When I started exposing services like Nextcloud and Jellyfin to the internet, I realized I needed real control over what gets in and out of my network.
A stateful firewall does three critical things:
- Maintains connection state: It tracks established connections and only allows replies to outbound requests, blocking unsolicited inbound traffic.
- Provides granular rules: You can allow port 443 for HTTPS while blocking everything else on that machine.
- Logs suspicious activity: Failed connection attempts, port scans, and brute-force attacks are recorded for review.
For my VPS deployments, I always use UFW (Uncomplicated Firewall) on Ubuntu. It's simple enough that you won't misconfigure it, but powerful enough to stop most opportunistic attacks.
UFW: The Firewall I Actually Use
UFW sits on top of iptables and makes firewall rules feel human-readable. Here's how I set it up on a fresh VPS that I'm using to run a reverse proxy for my homelab.
# Install UFW (usually pre-installed on Ubuntu/Debian)
sudo apt update && sudo apt install ufw -y
# Default policy: deny incoming, allow outgoing
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH on non-standard port (change 2222 to your port)
sudo ufw allow 2222/tcp
# Allow HTTP and HTTPS for reverse proxy
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Enable UFW
sudo ufw enable
# Check rules
sudo ufw status verbose
That's the foundation. Here's what I changed:
- I always move SSH to a non-standard port (like 2222). Port 22 gets hammered by bots 24/7.
- I only allow the three ports I actually need: SSH, HTTP, and HTTPS.
- Everything else—databases, APIs, internal services—stays internal to the VPS and only accessible via the reverse proxy on port 443.
To see what's trying to get in, I check the system log:
sudo ufw logging on
sudo tail -f /var/log/ufw.log | grep BLOCK
When I see repeated attempts on the same port (say, port 3306 for MySQL), that's external scanning. UFW is doing its job.
Port Forwarding: The Right Way
Port forwarding maps a public-facing port on your router to a private IP and port inside your network. The problem is: once you forward a port, anything on the internet can reach that service. No more router firewall protection—just the application's own security.
I learned this when someone discovered my Gitea instance through Shodan and tried to brute-force the admin account. I had forwarded port 3000 directly to my internal server without any authentication layer in front of it. That's when I switched to a reverse proxy on a VPS.
Here's my current architecture:
- Public VPS (~$40/year on RackNerd or similar) with UFW allowing only ports 22, 80, 443.
- Caddy or Nginx running as a reverse proxy on the VPS, listening on 443 with an SSL certificate.
- WireGuard or Tailscale tunnel from the VPS back to my homelab on a private network.
- Internal services (Nextcloud, Gitea, Jellyfin) only accessible through the encrypted tunnel.
This way, port forwarding is one-way: my homelab initiates the connection to the VPS and keeps it alive. The VPS never initiates anything inbound to my home network. If someone breaks into the VPS, they're in a sandboxed container, not on my home network.
If you must use traditional port forwarding, then:
- Forward only one port (usually 443 for HTTPS).
- Run a reverse proxy behind that port to route traffic to internal services.
- Put authentication (OAuth, Authelia, or MFA) in front of everything.
- Change the forwarded port to something non-standard (e.g., 8443 instead of 443).
UPnP: The Convenience Trap
UPnP (Universal Plug and Play) lets applications automatically punch holes in your router's firewall without you lifting a finger. It sounds great—until you realize that malware can also punch holes without your knowledge.
I tested this once on a sandbox VM: I installed a vulnerable Docker image that contained a trojanized application. Within seconds, it opened port 31337 on my router. No prompt, no log—it was just there.
My advice: disable UPnP entirely.
- Log into your router (usually 192.168.1.1 or 192.168.0.1).
- Find the UPnP/IGD setting and turn it off.
- Reboot the router.
Yes, some applications won't work without it. That's fine—they're telling you they don't respect your network security. Use port forwarding rules you define instead.
Fail2ban: Automatic Brute-Force Protection
Even with UFW, attackers will find your SSH port and try common passwords. Fail2ban watches logs and temporarily blocks IPs that fail too many times.
# Install fail2ban
sudo apt install fail2ban -y
# Copy the default config to a local copy (don't edit defaults directly)
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
# Edit the local config
sudo nano /etc/fail2ban/jail.local
In the config, find these lines and uncomment/adjust them:
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
[sshd]
enabled = true
port = 2222
maxretry = 3
This says: if someone fails 3 times to log in via SSH in 10 minutes, ban their IP for an hour. Restart fail2ban:
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd
I've had this on for two years. It blocks about 50–100 IP addresses per day. Without it, my logs would be completely unreadable.
sudo fail2ban-client set sshd unbanip <your-ip>. Keep the command handy.When to Ditch Port Forwarding Entirely
The best port forwarding is the one you don't do. I use Tailscale or Cloudflare Tunnel for remote access now:
- Tailscale: A WireGuard-based mesh VPN. My devices automatically join a private network. No ports open to the internet at all.
- Cloudflare Tunnel: Uses an outbound-only connection to Cloudflare's network. Cloudflare handles the public endpoint. Again, nothing exposed locally.
For services I want semi-public (like a Jellyfin instance for friends), I still use a VPS reverse proxy, but with strict rate limiting and authentication.
A VPS costs around $40–50 per year on providers like RackNerd, OVH, or Hetzner. That's worth the peace of mind of not forwarding ports directly to your home network.
Firewall Rules for Docker Containers
If you're running Docker on your homelab server, there's a gotcha: Docker modifies iptables directly and can bypass UFW rules. I learned this when I forwarded a port for a Docker container, but UFW showed it as closed. The port was actually open—Docker just went around UFW.
To prevent this, add this to `/etc/docker/daemon.json`:
{
"iptables": true,
"ip-forward": false
}
Restart Docker and UFW will now control Docker traffic properly. If you need to expose a port from a container, use UFW rules, not Docker's -p flag.
A Complete Homelab Firewall Setup
Here's what my actual homelab firewall looks like in practice:
- Router: UPnP disabled, default deny incoming, SSH moved to port 2222, only port 443 forwarded.
- VPS reverse proxy: UFW allows only 22, 80, 443. Caddy listens on 443 with TLS. Outbound tunnel to homelab via WireGuard.
- Homelab server: UFW allows only internal network (192.168.0.0/24) and the WireGuard tunnel. Docker containers communicate internally, no ports exposed.
- SSH hardening: Non-standard port, fail2ban, key-based auth only, no root login.
- Logging: UFW logs to syslog, fail2ban to its own log. I review both weekly.
This setup gives me remote access to everything via Tailscale or the VPS reverse proxy, but nothing is directly exposed to the internet.
Next Steps
If you're currently port forwarding to your home network, I strongly recommend migrating to either a VPS reverse proxy setup or Tailscale. The cost is minimal (~$40/year for a VPS), and the security improvement is enormous.
Start by enabling UFW on all your servers, setting a default deny policy, and only allowing the ports you absolutely need. Then audit your router's port forwarding rules and see what you can remove. Finally, test your firewall with tools like nmap from outside your network to verify nothing unexpected is open.
Discussion