Brute-force attacks on SSH services represent one of the most persistent threats facing Linux servers connected to the internet. Automated scripts scan for open SSH ports around the clock, cycling through username and password combinations in hopes of guessing weak credentials. Fail2Ban provides an effective layer of defence against these attacks by monitoring authentication logs and automatically blocking IP addresses that exhibit malicious behaviour.
This guide covers how to install and configure Fail2Ban to protect SSH and web services on Ubuntu servers. It walks through the complete setup process, explains how jails work, covers common configuration mistakes that reduce effectiveness, and describes the steps needed to harden your configuration for production use.
How Fail2Ban Works
Fail2Ban monitors log files for patterns that match known attack signatures. When a pattern is detected repeatedly, Fail2Ban updates the server firewall to block the offending IP address. The blocking is temporary by default but can be extended for persistent attackers who return after their ban expires.
The tool operates on a concept of jails. Each jail monitors a specific log file for specific patterns and applies an action when those patterns match. The default installation includes sensible jail configurations for SSH, Apache, and Nginx out of the box. You can enable, disable, or customise these jails based on your server setup and traffic patterns.
When Fail2Ban identifies an IP that exceeds the failure threshold within the defined time window, it adds that IP to the firewall rules managed by iptables or nftables. Connections from the blocked IP are refused at the network level, preventing the attacker from consuming server resources or attempting further logins. This automated response happens within seconds of detecting the malicious behaviour, which is far faster than manual intervention could achieve.
Installing Fail2Ban on Ubuntu
The installation process is straightforward on Ubuntu. Update your package list and install the package from the default repository.
apt update && apt install fail2ban
After installation, the Fail2Ban service starts automatically. You can verify the service is running with this command.
systemctl status fail2ban
If the service is not running, start it manually.
systemctl start fail2ban
For servers that run headless or require Fail2Ban to start on boot without a GUI session, ensure the service is enabled.
systemctl enable fail2ban
The package installs with sensible defaults that work for most configurations. However, reviewing and customising the default settings for your specific environment improves effectiveness significantly.
Understanding the Configuration Structure
Fail2Ban stores its configuration in /etc/fail2ban/. The main configuration file is jail.conf, which contains default settings for all available jails. You should never edit this file directly because package updates can overwrite your changes without warning.
Instead, create a jail.local file in the same directory. Settings in this file override the defaults in jail.conf. This approach keeps your customisations separate and ensures they survive system updates. All changes should go into jail.local rather than the defaults file.
The jail.local file uses the same syntax as jail.conf. Here is a basic structure for overriding SSH jail settings.
[sshd]
enabled = true
port = ssh
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600
The settings control how Fail2Ban behaves. maxretry defines the number of failures allowed within the findtime window before a ban is issued. bantime specifies how long the ban lasts in seconds. The values above represent a reasonable starting point for SSH protection on most servers.
The ignoreip directive allows you to specify IP addresses or ranges that should never be banned. Adding your office or home IP address here prevents accidental self-blocking while you test or work on your server.
ignoreip = 127.0.0.1/8 192.168.1.0/24
Replace the example ranges with your actual IP addresses. This setting supports both individual IP addresses and CIDR notation for networks.
Configuring HTTP Protection for Nginx or Apache
Fail2Ban can also protect web servers against abuse, including aggressive scrapers, vulnerability scanners, and denial-of-service attempts. HTTP attacks typically happen faster than SSH attacks because a single machine can make hundreds of requests per minute without triggering account lockouts. For Apache servers specifically, reviewing your Apache security configuration alongside Fail2Ban provides more comprehensive protection.
A normal user loading a web page generates fewer than 10 requests per minute. An automated script can easily exceed 100 requests in the same timeframe. Monitoring access logs for this behaviour lets you block malicious traffic before it affects server performance or costs you bandwidth and processing resources.
The findtime for HTTP bans should be shorter than for SSH because HTTP attacks unfold more quickly. A five-minute window and a threshold of 100 requests will catch aggressive scrapers and scripted attacks without affecting normal users who refresh pages frequently.
[nginx-http-auth]
enabled = true
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 100
bantime = 600
findtime = 300
For Apache, replace the log path with the corresponding Apache access log location. On Ubuntu, this is typically /var/log/apache2/access.log or /var/log/apache2/other_vhosts_access.log depending on your logging configuration.
[apache-auth]
enabled = true
port = http,https
logpath = /var/log/apache2/access.log
maxretry = 100
bantime = 600
findtime = 300
Adjust the maxretry value based on your observed traffic patterns. Servers with API endpoints or heavy legitimate traffic may require higher thresholds to avoid blocking real users. Monitor your logs during the first few days after enabling HTTP jails to understand what normal traffic looks like on your server.
Application-level rate limiting can supplement Fail2Ban for protecting specific endpoints that require finer control than network-level blocking provides. This approach works particularly well for API services where you need to limit requests per user or per token rather than per IP address.
Enabling and Testing Your Jails
After creating your jail.local configuration, restart the Fail2Ban service to apply the changes.
systemctl restart fail2ban
List all configured jails and their status using the Fail2Ban client.
fail2ban-client status
This command shows which jails are active and how many IPs are currently banned. To check the status of a specific jail, such as the SSH jail, run this.
fail2ban-client status sshd
The output shows the total number of failures that triggered bans and lists the currently banned IP addresses. Reviewing this information regularly helps you understand what kind of traffic your server receives and whether your configuration needs adjustment.
Setting Up Email Notifications
Configuring Fail2Ban to send email notifications confirms that the service is working and alerts you to active attacks. Without notifications, you may not realise Fail2Ban is blocking traffic unless you check the logs manually, which most people do infrequently.
The default action for most jails bans the IP silently. To receive notifications, change the action to include whois information and log excerpts.
action = %(action_mwl)s
This configuration bans the offending IP and sends an email containing the whois record and relevant log lines. For this to work, your server must be able to send email. Configuring Postfix or another mail transfer agent ensures ban notifications reach your inbox rather than failing silently.
If your server does not have email configured, logs are still written to /var/log/fail2ban.log. You can monitor this file directly or set up log rotation to manage file sizes over time.
tail -f /var/log/fail2ban.log
Reading log entries helps you understand what patterns triggered bans and whether your thresholds are set appropriately. Persistent attack patterns in your logs may indicate the need for additional jail configurations or more restrictive settings.
Common Configuration Mistakes That Reduce Effectiveness
Several configuration errors commonly reduce Fail2Ban's effectiveness or cause unexpected blocking of legitimate users. Avoiding these mistakes helps you get the most from the tool.
- Setting maxretry too low: Legitimate users on unstable network connections may trigger multiple connection attempts, especially when an SSH client retries automatically. Setting
maxretryto at least 3 for SSH prevents accidental bans of real users who have temporary connectivity issues. - Forgetting to whitelist your own IP: Accidentally banning yourself while testing or during normal work is frustrating and potentially disruptive. Add your office or home IP address to the
ignoreipdirective in yourjail.localfile to prevent self-blocks. - Incorrect log file paths: Fail2Ban silently takes no action if the log path in your jail configuration does not match the actual log file location. Verify paths carefully, especially if your distribution or web server uses non-standard logging locations.
- Editing jail.conf directly: Changes made to
jail.confare overwritten during package updates. Always usejail.localfor customisations to preserve settings across updates and make your configuration portable. - Ignoring ban repeat offenders: Some attackers cycle through IP addresses or wait for bans to expire before resuming. Persistent attackers should be added to a permanent block list at the firewall level rather than relying on Fail2Ban to re-ban them repeatedly.
- Setting findtime too long: A long findtime window combined with a low maxretry can cause Fail2Ban to miss attack patterns that span across the window boundary. Keep findtime proportional to your expected traffic patterns.
Hardening Fail2Ban for Production Servers
The default Fail2Ban configuration is a useful starting point, but production servers benefit from more restrictive settings. Adjusting the thresholds based on your actual traffic patterns and security requirements strengthens your defence significantly. Combining Fail2Ban with a broader server hardening checklist helps you address multiple security layers together.
For SSH access exposed to the internet, set maxretry to 3 and bantime to at least 3600 seconds, which is one hour. For servers that experience repeated targeted attacks, consider extending the ban time to 86400 seconds, which is 24 hours, for repeat offenders. The findtime of 600 seconds (10 minutes) remains appropriate for most SSH configurations.
Use the Fail2Ban client to manually ban IP addresses that you observe scanning your server, even if they have not yet triggered an automatic ban. This proactive approach stops persistent scanners before they can catalogue your server's vulnerabilities.
fail2ban-client set sshd banip 192.0.2.100
Replace 192.0.2.100 with the IP address you want to block. After manually banning an IP, review your logs to understand why it was flagged and whether it represents a genuine threat or a misconfiguration.
Combine Fail2Ban with a properly configured firewall for layered protection. UFW (Uncomplicated Firewall) on Ubuntu works well alongside Fail2Ban, providing baseline firewall rules while Fail2Ban dynamically blocks IP addresses based on observed behaviour. Reviewing both configurations together gives you a clearer picture of your server's exposure and helps you identify gaps in your protection.
ufw status verbose
This command shows your current UFW rules, which should complement rather than conflict with Fail2Ban's dynamic blocking. If you are setting up a new server, hardening Ubuntu after install covers the essential steps for a secure baseline.
Integrating Fail2Ban with Secure SSH Configuration
Fail2Ban is most effective as part of a broader security strategy rather than as a standalone solution. Combining it with SSH key authentication and secure SSH configuration creates multiple layers of defence. Even if Fail2Ban fails or is temporarily unavailable, strong SSH configuration continues to protect your server.
Disabling password authentication for SSH and using key-based login eliminates the primary attack vector that Fail2Ban protects against. Even with Fail2Ban in place, strong SSH configuration reduces your attack surface significantly. Configuring SSH securely covers the key settings you should implement alongside Fail2Ban.
You should also restrict SSH access to specific IP addresses or ranges where possible, limiting who can attempt authentication at all. If your office or home has a static IP address, configuring your firewall to allow SSH only from that address blocks all other SSH connection attempts before they reach Fail2Ban or the SSH daemon itself.
Regularly reviewing /var/log/fail2ban.log helps identify patterns in attack traffic and refine your configuration over time. Look for IP ranges that appear repeatedly, scan schedules that suggest automated tools, and any new attack signatures that may require additional jail configurations.
When you identify persistent attackers, consider adding their IP ranges to a permanent block list in your firewall rather than relying on Fail2Ban to re-block them each time. This approach reduces processing overhead and ensures these IPs remain blocked even during Fail2Ban restarts or temporary outages.
When Fail2Ban Is Not Enough
Fail2Ban works well against attacks where a single IP address generates many requests. However, it cannot stop distributed attacks where each IP address makes only a small number of requests. In a distributed brute-force attack, thousands of different IP addresses might each attempt just one or two logins, staying below the maxretry threshold.
For protection against distributed attacks, consider cloud-based DDoS mitigation services that can absorb and filter malicious traffic before it reaches your server. These services operate at the network edge and can distinguish between legitimate traffic and attack traffic based on characteristics that individual server logs do not reveal.
Fail2Ban also cannot protect against attacks that exploit application vulnerabilities or credential stuffing where attackers use username and password combinations stolen from other services. Application-level security measures, proper input validation, and multi-factor authentication address these threat vectors more effectively than network-level blocking alone.
For Apache servers specifically, ensuring your HTTP daemon configuration follows security best practices reduces the attack surface available to exploit. Fail2Ban handles automated attacks, but a secure default configuration prevents many attacks from succeeding even if they slip through.
Maintaining Your Fail2Ban Setup Over Time
Fail2Ban configuration requires occasional review as your server usage evolves. Traffic patterns change, new services are added, and attack techniques shift. A configuration that works well initially may need adjustment as your server grows and attracts more attention from automated scanners.
When adding new services to your server, consider whether they expose new attack surfaces. A new web application, API endpoint, or management interface may benefit from its own Fail2Ban jail. Review the available jail templates and adapt them for your specific log formats and access patterns.
Keep your Fail2Ban installation updated through regular system package updates. New jail definitions and pattern matching improvements are included in updates that address emerging threats. Subscribe to security mailing lists for your distribution to stay informed about updates that affect Fail2Ban or its dependencies.
Back up your jail.local file before performing major system upgrades. While the file should be preserved in most cases, having a backup ensures you can restore your configuration quickly if anything goes wrong during the upgrade process. Store backups in a location separate from the server itself, such as a local machine or cloud storage.
Related practical reading
These related guides can help you connect this topic with the wider website, server, security, and support decisions around it.
- SSH Config Tips That Save Hours of Time - useful background for related technology decisions
- How to Build a PHP Webhook Receiver: Complete Implementation Guide - useful background for related technology decisions