Introduction
Running a homelab means exposing services to the internet, which inevitably attracts unwanted attention. Within days of setting up my first SSH server in the cloud, I started seeing hundreds of failed login attempts in my logs from IP addresses around the world. These brute force attacks weren’t just annoying - they were a real security risk.
This is where fail2ban became a game-changer in my homelab security setup. Instead of manually tracking and blocking suspicious IPs, fail2ban does the heavy lifting by automatically detecting and preventing these attacks before they become a problem.
What is Fail2ban?
Fail2ban ↗️ is an open-source intrusion prevention tool that monitors your system logs and automatically blocks IP addresses that show signs of malicious activity. Think of it as an automated security guard that never sleeps.
Here’s how it works:
- Monitors log files (like
/var/log/auth.log
) for suspicious patterns - Detects repeated failures using configurable regex filters
- Automatically bans IPs by updating firewall rules (iptables)
- Unbans after timeout or manually when needed
- Sends notifications when actions are taken
Key Features:
- Multiple “jails” for different services (SSH, web servers, etc.)
- Configurable ban duration and failure thresholds
- IP whitelisting to prevent self-banning
- Integration with notification systems
- Support for IPv4 and IPv6
Important Note
Fail2ban reduces attack frequency but isn’t a complete security solution. You still need strong authentication, SSH keys, and other security practices.
Setup Fail2ban
I recommend installing fail2ban directly on your host system for better log access and firewall integration.
Installation
Ubuntu/Debian:
sudo apt updatesudo apt install fail2ban
CentOS/RHEL:
sudo yum install epel-releasesudo yum install fail2ban
Basic Configuration
Create local configuration files (prevents updates from overwriting your settings):
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.localsudo cp /etc/fail2ban/fail2ban.conf /etc/fail2ban/fail2ban.local
Edit /etc/fail2ban/jail.local
with your basic settings:
[DEFAULT]# Ban IP for 1 hourbantime = 3600
# 5 failed attempts within 10 minutes triggers banfindtime = 600maxretry = 5
# Whitelist your trusted IPs (IMPORTANT: Add your admin IPs)ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 YOUR_ADMIN_IP
[sshd]enabled = trueport = sshfilter = sshdlogpath = /var/log/auth.logmaxretry = 3
Start and Test
# Enable and start fail2bansudo systemctl enable fail2bansudo systemctl start fail2ban
# Check statussudo fail2ban-client statussudo fail2ban-client status sshd

Eternal Vault is Now Live! 🎉
Secure your digital legacy with Eternal Vault — now launched and available for everyone! Store your passwords, documents, and critical info in a zero-knowledge encrypted vault. If you ever stop checking in, your chosen loved ones are notified and given access, ensuring your legacy is protected and responsibly passed on.
Discord Notifications Setup
Getting instant alerts when IPs get banned helps monitor your homelab’s security. Here’s how to set up Discord notifications.
Create Discord Webhook
- Open Discord → Go to your desired channel
- Channel Settings → Integrations → Webhooks
- Create Webhook → Copy the URL
Configure Discord Action
Create the Discord notification script at /etc/fail2ban/scripts/discord-notify.sh
:
#!/bin/bash
# Discord webhook URL - update this with your webhook URLWEBHOOK_URL="https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN"
# Script parametersACTION="$1"IP="$2"JAIL="$3"HOSTNAME="$4"FAILURES="$5"BANTIME="$6"
send_discord_notification() { local embed_json="$1"
curl -X POST "$WEBHOOK_URL" \ -H "Content-Type: application/json" \ -d "$embed_json" \ --silent --output /dev/null}
case "$ACTION" in "ban") embed_json=$(cat <<EOF{ "username": "Fail2Ban", "embeds": [{ "title": "🔨 IP Address Banned", "description": "A malicious IP has been automatically blocked by Fail2ban", "color": 15158332, "fields": [ { "name": "🎯 Banned IP", "value": "\`$IP\`", "inline": true }, { "name": "🏠 Host", "value": "$HOSTNAME", "inline": true }, { "name": "🔒 Jail", "value": "$JAIL", "inline": true }, { "name": "❌ Failed Attempts", "value": "$FAILURES", "inline": true }, { "name": "⏱️ Ban Duration", "value": "$BANTIME seconds", "inline": true }, { "name": "🔍 IP Lookup", "value": "[Check IP Info](https://db-ip.com/$IP)", "inline": true } ] }]}EOF ) send_discord_notification "$embed_json" ;; "unban") embed_json=$(cat <<EOF{ "username": "Fail2Ban", "embeds": [{ "title": "🔓 IP Address Unbanned", "description": "An IP address has been automatically unbanned", "color": 3066993, "fields": [ { "name": "🎯 Unbanned IP", "value": "\`$IP\`", "inline": true }, { "name": "🏠 Host", "value": "$HOSTNAME", "inline": true }, { "name": "🔒 Jail", "value": "$JAIL", "inline": true } ] }]}EOF ) send_discord_notification "$embed_json" ;;esac
Make it executable:
sudo mkdir -p /etc/fail2ban/scriptssudo chmod +x /etc/fail2ban/scripts/discord-notify.sh
Create the fail2ban action file /etc/fail2ban/action.d/discord.conf
:
[Definition]actionban = /etc/fail2ban/scripts/discord-notify.sh ban <ip> <name> <fq-hostname> <failures> <bantime>actionunban = /etc/fail2ban/scripts/discord-notify.sh unban <ip> <name> <fq-hostname>
Test your setup:
# Test ban notification/etc/fail2ban/scripts/discord-notify.sh ban "192.168.1.100" "sshd" "homelab-server" "5" "3600"
# Test unban notification/etc/fail2ban/scripts/discord-notify.sh unban "192.168.1.100" "sshd" "homelab-server"
Configure Jails
Instead of modifying jail.local
, create individual jail configuration files in /etc/fail2ban/jail.d/
. This is a better practice for organization and maintenance.
Create SSH protection (/etc/fail2ban/jail.d/ssh.conf
):
[sshd]enabled = trueport = sshfilter = sshdlogpath = /var/log/auth.logmaxretry = 3findtime = 600bantime = 3600action = %(action_mwl)s discord
Create web server protection (/etc/fail2ban/jail.d/nginx.conf
):
[nginx-http-auth]enabled = truefilter = nginx-http-authport = http,httpslogpath = /var/log/nginx/error.logmaxretry = 5action = %(action_mwl)s discord
[nginx-noscript]enabled = truefilter = nginx-noscriptport = http,httpslogpath = /var/log/nginx/access.logmaxretry = 6action = %(action_mwl)s discord
Why the jail.d
Approach is Better:
- Modular configuration: Each service gets its own config file
- Easy management: Enable/disable services by renaming files
- Version control friendly: Track changes per service
- No merge conflicts: Updates won’t overwrite your custom configs
- Clean organization: Easy to see what’s protected at a glance
- Selective enabling: Just delete/rename files to disable specific jails
Restart fail2ban: sudo systemctl restart fail2ban
How I Use Fail2ban in My Homelab
In my current setup, fail2ban protects several critical services:
Protected Services:
- SSH on all servers (most important)
- Nginx reverse proxy (protects all web services behind it)
- Vaultwarden password manager
- Jellyfin media server
My Configuration:
[DEFAULT]bantime = 86400 # 24-hour bansfindtime = 3600 # 1-hour detection windowmaxretry = 3 # Strict 3-strike policyignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 100.64.0.0/10 # Include Tailscale range
Real Results Since Implementation:
- Thousands of malicious IPs blocked automatically
- Instant Discord alerts keep me informed
- Zero manual intervention needed for basic attacks
Best Practices and Tips
Essential Security Practices
- Always whitelist your admin IPs in
ignoreip
to prevent lockouts - Use SSH keys instead of passwords when possible
- Test your configuration before going live:
sudo fail2ban-client -t
- Monitor logs regularly:
sudo tail -f /var/log/fail2ban.log
Useful Commands
# Check all active jailssudo fail2ban-client status
# Check specific jail detailssudo fail2ban-client status sshd
# Manually ban/unban an IPsudo fail2ban-client set sshd banip 192.168.1.100sudo fail2ban-client set sshd unbanip 192.168.1.100
# Check banned IPssudo iptables -L -n | grep DROP
Common Gotchas
- Self-banning: Always whitelist your admin IPs
- Log permissions: Ensure fail2ban can read your log files
- Firewall conflicts: Make sure fail2ban can modify iptables rules
- Testing carefully: Don’t test bans from your primary admin IP
Conclusion
Fail2ban has become essential in my homelab security setup. It’s not just about stopping attacks - it provides visibility into what’s targeting your services and responds automatically to threats.
The setup is straightforward, and the peace of mind is invaluable. Combined with Discord notifications, I get real-time security insights without constantly monitoring logs.
Remember: fail2ban is one layer in your security stack. Use it alongside strong firewall rules, VPN access, and proper authentication for comprehensive protection.
Have you set up fail2ban in your homelab? What services do you protect, and how do you handle notifications? Share your experiences in the comments below, or reach out to me on Twitter ↗️ / Reddit ↗️.
Stay secure, and happy homelabbing! 🛡️