Skip to Content

Self-Host Fail2ban: Block Brute-Force Attacks in 15 Minutes

Deploy Fail2ban with Docker to automatically block brute-force attacks on your self-hosted services

Self-Host Fail2ban: Block Brute-Force Attacks in 15 Minutes

Every server on the internet faces constant brute-force attacks. SSH login attempts, WordPress login floods, SMTP auth hammering — bots are relentless. Fail2ban is your lightweight, battle-tested defense that automatically bans offending IPs after repeated failed attempts. In this guide, you'll deploy Fail2ban with Docker and protect your services in under 15 minutes.

Why Self-Host Fail2ban?

  • Zero-trust defense: Don't rely solely on cloud firewalls — add a local layer
  • Universal protection: Works with SSH, Nginx, Traefik, WordPress, mail servers, and more
  • Lightweight: Uses minimal resources (~10MB RAM)
  • Proven: 20+ years of production use, default on most Linux distributions

What You'll Need

  • A VPS or server (1 vCPU, 512MB RAM minimum)
  • Docker & Docker Compose installed
  • Root or sudo access
  • ~5 minutes of focused time

Step 1: Create the Docker Compose File

Create a directory for Fail2ban:

mkdir -p ~/fail2ban/config
cd ~/fail2ban

Create docker-compose.yml:

version: '3.8'

services:
  fail2ban:
    image: crazymax/fail2ban:latest
    container_name: fail2ban
    network_mode: host
    cap_add:
      - NET_ADMIN
      - NET_RAW
    volumes:
      - ./config:/data
      - /var/log:/var/log:ro
      - /var/log/auth.log:/var/log/auth.log:ro
    environment:
      - TZ=UTC
      - F2B_LOG_LEVEL=INFO
      - F2B_DB_PURGE_AGE=1d
    restart: unless-stopped

Key configuration:

  • network_mode: host — Required for iptables/nftables to work
  • cap_add: NET_ADMIN, NET_RAW — Required for modifying firewall rules
  • /var/log:/var/log:ro — Read access to system logs

Step 2: Configure Fail2ban

Create the main configuration:

cat > config/jail.local << 'EOF'
[DEFAULT]
# Ban IP after 3 failed attempts within 10 minutes
findtime = 10m
maxretry = 3
# Ban for 1 hour
bantime = 1h
# Use iptables
backend = auto
# Send email alerts (optional)
# destemail = [email protected]
# sender = [email protected]
# action = %(action_mwl)s

[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 1h

[nginx-http-auth]
enabled = true
filter = nginx-http-auth
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 3

[nginx-botsearch]
enabled = true
filter = nginx-botsearch
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 2

[traefik-auth]
enabled = true
filter = traefik-auth
port = http,https
logpath = /var/log/traefik/access.log
maxretry = 3
EOF

Create a custom filter for Traefik:

mkdir -p config/filter.d
cat > config/filter.d/traefik-auth.conf << 'EOF'
[Definition]
failregex = ^<HOST> .* "(GET|POST|PUT|DELETE).* HTTP/[0-9.]+" 401
            ^<HOST> .* "(GET|POST|PUT|DELETE).* HTTP/[0-9.]+" 403
ignoreregex =
EOF

Step 3: Start Fail2ban

docker compose up -d

Verify it's running:

docker compose logs --tail 20

You should see output like:

fail2ban  | 2024-01-15 10:30:00,123 fail2ban.server [1]: INFO Starting Fail2ban v1.0.2
fail2ban  | 2024-01-15 10:30:00,456 fail2ban.jail [1]: INFO Creating new jail 'sshd'
fail2ban  | 2024-01-15 10:30:00,789 fail2ban.jail [1]: INFO Jail 'sshd' started

Step 4: Verify Protection

Check jail status:

docker exec -it fail2ban fail2ban-client status

Check specific jail (e.g., SSH):

docker exec -it fail2ban fail2ban-client status sshd

Test the ban (from another machine, try failed SSH logins):

# On another machine, run this 4 times:
ssh wronguser@your-server-ip

Check if the IP got banned:

docker exec -it fail2ban fail2ban-client status sshd

You should see the banned IP in the "Banned IP list" section.

Step 5: View and Manage Bans

List banned IPs:

docker exec -it fail2ban fail2ban-client status sshd | grep "Banned IP list"

Unban an IP manually:

docker exec -it fail2ban fail2ban-client set sshd unbanip 192.168.1.100

Check logs in real-time:

docker exec -it fail2ban tail -f /var/log/fail2ban.log

Advanced: Permanent Bans and Whitelisting

Add to config/jail.local:

[DEFAULT]
# Your home IP (never ban)
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 your-home-ip

# Permanent ban after 3 temporary bans
bantime.increment = true
bantime.factor = 1
bantime.maxtime = 1w
bantime.multipliers = 1 2 4 8 16 32 64

Restart to apply:

docker compose restart

Integration with Cloudflare (Optional)

If using Cloudflare, ban at the edge:

cat > config/action.d/cloudflare.conf << 'EOF'
[Definition]
# Get your Cloudflare API token with Zone:Edit permissions
# Required: actionstart, actionstop, actioncheck, actionban, actionunban
actionstart =
actionstop =
actioncheck =
actionban = curl -s -X POST "https://api.cloudflare.com/client/v4/zones/<cftoken>/firewall/access_rules/rules" \
            -H "Authorization: Bearer <cfapikey>" \
            -H "Content-Type: application/json" \
            --data '{"mode":"block","configuration":{"target":"ip","value":"<ip>"},"notes":"Fail2ban <name>"}'
actionunban = curl -s -X DELETE "https://api.cloudflare.com/client/v4/zones/<cftoken>/firewall/access_rules/rules/<ip>" \
              -H "Authorization: Bearer <cfapikey>"

[Init]
cftoken = your-zone-id
cfapikey = your-api-token
EOF

Verification Checklist

  • [ ] Container running: docker ps | grep fail2ban
  • [ ] Jails active: docker exec fail2ban fail2ban-client status
  • [ ] SSH protection working: failed logins trigger ban
  • [ ] Logs visible: docker logs fail2ban
  • [ ] Firewall rules applied: sudo iptables -L -n | grep fail2ban

Next Steps

  • Monitor: Set up Uptime Kuma for service monitoring
  • Visualize: Add Fail2ban metrics to Grafana
  • Centralize: Forward logs to a central server for analysis
  • Expand: Protect WordPress, mail servers, VPN endpoints

Quick Reference

CommandPurpose
docker compose up -dStart Fail2ban
docker exec fail2ban fail2ban-client statusList all jails
docker exec fail2ban fail2ban-client status sshdSSH jail status
docker exec fail2ban fail2ban-client set sshd unbanip IPUnban an IP
docker exec fail2ban fail2ban-client reloadReload config

Fail2ban is now protecting your server. Most brute-force attempts will be blocked automatically, and you'll have visibility into attack patterns. For a complete security stack, consider pairing this with CrowdSec for community-driven threat intelligence.

Last updated: August 2026 | Sysbrix Self-Hosting Guides

Self-Host MinIO: S3-Compatible Object Storage in 15 Minutes