Deployment guide

Fail2Ban Enhanced on VPS: blocking repeated attacks

Deploy on a VPS Cloud →

Tutorial

Security & Monitoring

Fail2Ban Enhanced on VPS: blocking repeated attacks

As soon as a VPS exposes SSH or a web application, automated attempts arrive quickly. Fail2Ban Enhanced provides a simple safety net: read the logs, detect repeated failures and ban the IPs that persist. This guide goes from the initial setup to day-to-day operation: jails and thresholds, choosing the backend, checking that a jail is still watching, troubleshooting with the real error messages, fitting it with the VPS firewall, and backing up the configuration. The options cited come from the upstream Fail2Ban documentation.

11 min read11 steps
Contents· Why self-host Fail2ban on a VPS1/14
  1. 01Why self-host Fail2ban on a VPS
  2. 02The concrete benefits of this template
  3. 03Quantified requirements to host it
  4. 04Install and configure Fail2ban on your VPS
  5. 05ignoreip, ban duration and repeat offenders
  6. 06Progressive banning: formula, cap and memory
  7. 07Choosing the backend: auto, pyinotify, polling or systemd
  8. 08The four Fail2ban backends
  9. 09Checking that the jail is still watching
  10. 10Check in four steps that the jail still reads
  11. 11Troubleshooting: the real error messages
  12. 12Common error messages and fixes
  13. 13nftables or iptables, the VPS firewall and backup
  14. 14The official documentation

Why self-host Fail2ban on a VPS

Fail2ban is one of the simplest and most widespread protection tools for an exposed server: it continuously reads your logs, applies regular expressions (the filters) to spot intrusion attempts, and bans the offending IP by adding it to nftables or iptables for a configurable duration. On a VPS, it is a first line of defense to put in place before the rest, because it depends on no external service, uses little and protects SSH against bruteforce as soon as the jail is enabled. Beyond SSH, you can extend it to Nginx (authentication attempts, URL scans), Postfix, WordPress or any service that writes logs. Everything runs locally on your machine: no data leaves, no third-party service is involved. It is a self-contained, easy-to-audit solution, provided you check on it from time to time, as we will see below.

The concrete benefits of this template

  • Automatic banning — IPs that bruteforce SSH are banned as soon as the maxretry threshold is reached within the findtime window.
  • Filters for many services — SSH, Nginx, Apache, Postfix, WordPress, and custom filters written in filter.d.
  • Minimal footprint — it runs without trouble on the smallest of VPSes.
  • Progressive banning (repeat offense) — the more an IP reoffends, the longer the ban.
  • Email notifications — possible on each ban, with the action_mw or action_mwl actions from jail.conf.
  • Self-contained and local — no network dependency day to day, simple to audit and version.

Quantified requirements to host it

Fail2ban is frugal: a VPS with 1 vCPU and 512 MB of RAM is enough to run it, with no perceptible impact (an editorial recommendation, to be measured against your own load). On the software side, the upstream README for version 1.1.1 asks for Python 3.5 or later. Two components are optional: pyinotify 0.8.3 or later to watch files (Linux kernel 2.6.13 or later), and systemd 204 or later with the python-systemd package to read the journal. Fail2ban installs directly on the system (no need for Docker) and requires root access, a firewall backend (nftables or iptables) and access to the logs of the services to protect: the systemd journal or /var/log/auth.log for SSH depending on the distribution, and the Nginx access and error logs. It keeps its state in a SQLite database, /var/lib/fail2ban/fail2ban.sqlite3 by default. No additional domain or port is needed. The golden rule: fill in ignoreip with your IP before enabling banning, so you don't lock yourself out of your own server.

Install and configure Fail2ban on your VPS

  1. Install Fail2ban

    On Debian or Ubuntu: sudo apt update && sudo apt install fail2ban. The service starts automatically. Check with sudo systemctl status fail2ban.

  2. Create a jail.local

    Never modify jail.conf directly. Write your settings in /etc/fail2ban/jail.local, which overrides them: that's where you define bantime, findtime, maxretry and above all ignoreip = 127.0.0.1/8 ::1 VOTRE_IP to protect yourself from a self-ban. The defaults in jail.conf are bantime = 10m, findtime = 10m and maxretry = 5.

  3. Enable SSH protection

    All jails are disabled by default in jail.conf: explicitly enable [sshd] in jail.local with enabled = true, for example maxretry = 3 and bantime = 1h. After 3 failures within the findtime window, the IP is banned for one hour at the firewall level.

  4. Add a jail for Nginx

    Enable the [nginx-http-auth] and [nginx-botsearch] jails, provided by jail.conf, and make sure logpath points to your Nginx logs. This blocks repeated authentication attempts and malicious URL scans.

  5. Test the configuration, then reload it

    Check the syntax first: sudo fail2ban-client -t. Then apply it: sudo fail2ban-client reload (or sudo systemctl restart fail2ban). sudo fail2ban-client status lists the active jails and sudo fail2ban-client status sshd shows the currently banned IPs.

  6. Test a ban and the unban

    Check the filter against your own log with sudo fail2ban-regex systemd-journal sshd. To simulate a ban without attacking anyone: sudo fail2ban-client set sshd banip 203.0.113.10, then read sudo fail2ban-client status sshd. To release a legitimate IP blocked by mistake: sudo fail2ban-client set sshd unbanip X.X.X.X.

  7. Logging in for the first time

    There is no web interface and no account to create: fail2ban is a system daemon that, with the template's setting, bans IP addresses after 4 failed SSH attempts within 10 minutes. Over SSH: fail2ban-client status sshd (banned IPs), fail2ban-client set sshd unbanip <IP> (to unban). Adjust these thresholds in jail.local.

ignoreip, ban duration and repeat offenders

ignoreip accepts IP addresses, CIDR masks or DNS host names, separated by spaces or commas. By default ignoreself = true: the server's own local addresses are never banned, but your office or home IP is not covered by that. If it changes often, declare a dynamic host name or use ignorecommand, an external command that receives the IP and answers whether it should be ignored. Also keep a fallback access that does not depend on SSH, such as your VPS provider's console: a self-ban can then be fixed without outside help. The duration is set with bantime, in seconds or with the suffixes m, h, d, w, mo and y. It acts as the initial duration when progressive banning is enabled. For repeat offenders, jail.conf also ships a recidive jail, which reads fail2ban's own log.

Progressive banning: formula, cap and memory

Enable progressive banning in jail.local with bantime.increment = true. The default formula multiplies bantime by 1, 2, 4, 8, 16 and so on at each reoffense; bantime.factor (1 by default) is a common multiplier, and bantime.maxtime caps the duration in seconds, for example bantime.maxtime = 604800 for seven days. One point is often missed: fail2ban finds earlier bans in its database, which is purged according to dbpurgeage (one day by default in fail2ban.conf). For widely spaced reoffenses to be found, raise it; the comment on the recidive jail in jail.conf cites 648000 seconds. Pair all this with disabling password authentication (SSH keys only): Fail2ban then becomes a safety net rather than your only rampart.

Choosing the backend: auto, pyinotify, polling or systemd

The backend is the way a jail reads its logs. The default in jail.conf is backend = auto, which each jail can override. On Debian and Ubuntu, the paths-debian.conf file in the upstream 1.1.1 archive sets sshd_backend = systemd: sshd is then read from the systemd journal, not from /var/log/auth.log. Read sudo fail2ban-client status sshd to learn what is really being watched: the File list line indicates files, the Journal matches line indicates the journal. A distribution may change these values, so assume nothing: observe. Changing backend is done jail by jail, in jail.local, followed by sudo fail2ban-client reload.

The four Fail2ban backends

Scroll the table

BackendWhat it watchesWorth knowing
autoFiles (pyinotify, then polling); switches to systemd if no `logpath` file exists and the filter defines `journalmatch`Default value in `jail.conf`
pyinotifyFiles, through kernel inotify eventsLinux only, Python pyinotify module required; better suited to files than polling according to the documentation
pollingFiles, by re-reading at regular intervalsNo external library, the simplest fallback
systemdThe systemd journal, through python-systemd`logpath` is not valid; the filter goes through `journalmatch`; `journalflags` and `rotated` settings

Checking that the jail is still watching

A started jail is not necessarily a jail that reads. Reported, not confirmed by the maintainers: in fail2ban issue 4250, opened in October 2026, an administrator on Debian with fail2ban 1.1.1 describes an sshd jail on the systemd backend that stops banning one to two days after start, while the Nginx and Postfix jails keep working. Two other administrators, both on FreeBSD according to their messages, report a similar symptom with the pyinotify backend; one says things became stable after switching to polling. A maintainer pointed out that the original issue was about the systemd backend and that the two symptoms may not be related. A last participant, on Arch with the systemd backend, describes the same stop after a journal rotation and puts forward a hypothesis about the rotated setting: it remains unconfirmed. The cause is not established to date, and nothing allows generalizing to your server. The check itself takes a minute.

Check in four steps that the jail still reads

  1. Read the jail status

    sudo fail2ban-client status sshd shows Currently failed and Total failed for the filter, File list or Journal matches for the source, then Currently banned, Total banned and Banned IP list for the action. Note Total failed.

  2. Compare with the log

    Count the real failures over the same period: sudo journalctl _COMM=sshd --since "1 hour ago" | grep -c "Failed password", or sudo grep -c "Failed password" /var/log/auth.log if the jail reads that file. Then read Total failed again: it should have grown. Compare the trend, not exact equality, because IPs in ignoreip do not count.

  3. Read fail2ban's own log

    sudo tail -n 50 /var/log/fail2ban.log: a healthy jail writes Found lines followed by Ban. Several days of lone Unban lines, while the authentication log shows attempts, point to a blind jail.

  4. Restart and change backend if needed

    sudo systemctl restart fail2ban restarts the reading; the issue describes normal operation for one to two days after each start. For a file-based jail, backend = polling is the simplest fallback. Schedule this check (cron or a monitoring tool) rather than relying on the absence of alerts.

Troubleshooting: the real error messages

These messages come from the Fail2ban 1.1.1 source code (clients and configuration readers); the default logging level is INFO. For each case the starting command is the same: sudo fail2ban-client -t tests the configuration, sudo journalctl -u fail2ban gives the beginning of the story.

Common error messages and fixes

  • Have not found any log file for 'sshd' jail. — the jail's logpath points to no existing file and startup fails, unless skip_if_nologs = true. With the auto backend, a jail whose filter defines journalmatch switches to systemd instead. Fix the path or set backend = systemd.
  • No file(s) found for glob /var/log/nginx/error.log — a logpath pattern matches nothing. Check the real location of the Nginx logs with ls /var/log/nginx.
  • Unable to read the filter 'nginx-xyz' — the name given to filter = does not exist in filter.d. Fix the typo or add your filter file.
  • Failed to access socket path: /var/run/fail2ban/fail2ban.sock. Is fail2ban running? — the client cannot find the server socket. The service is stopped or failing: sudo systemctl status fail2ban, then its log.
  • Permission denied to socket: ... (you must be root) — the fail2ban-client command was run without sufficient rights. Run it again with sudo.
  • Unable to contact server. Is it running? — the socket exists but the server does not answer. Restart the service, then read its startup log.
  • [sshd] Journal reader seems to be offline, reopen journal — fail2ban detected that its reading of the systemd journal had stopped (rotation) and reopens it. This message is at the MSG level, below INFO: its absence from a normal log proves nothing, hence the check in the previous section.
  • Error reading line from systemd journal — a read error on the systemd journal, followed by the system cause. Check the service's rights and the journal state with journalctl --verify.

nftables or iptables, the VPS firewall and backup

The ban action is chosen with banaction. jail.conf offers iptables-multiport as a commented example; the paths-debian.conf file in the 1.1.1 archive sets banaction = nftables and banaction_allports = nftables[type=allports]. The nftables action creates by default the f2b-table table (inet family) and the f2b-chain chain, hooked on input with priority -1; sudo nft list table inet f2b-table shows the banned IPs. Pick a single family of actions, the same as the rest of your firewall, otherwise you look for your rules in the wrong table. The VPS firewall acts upstream: it filters what reaches the machine, for example by limiting SSH to your addresses, while Fail2ban reacts to what the logs say. The two complement each other, but keep ignoreip consistent with your rules. Behind a proxy or a CDN, the logs show the proxy's IP: restore the real IP before banning, otherwise you ban the proxy. For backup, archive the configuration (sudo tar czf fail2ban-config.tar.gz /etc/fail2ban) and the output of sudo fail2ban-client --dp, which documents the effective configuration. The /var/lib/fail2ban/fail2ban.sqlite3 database holds the memory of past bans.

The official documentation

For advanced configuration, options specific to the tool, and version changes, refer to the official Fail2Ban documentation. This guide covers deployment on a ServOrbit VPS; the vendor's documentation remains the reference for fine-tuning.

Armor access to your VPS

The ServOrbit Cloud VPS gives you root access and a ready-to-use firewall to configure Fail2ban in a few minutes and stop bruteforce attacks cold.