Fail2ban reads log files, notices addresses failing repeatedly, and blocks them at the firewall for a while. It is the standard answer to the constant automated password guessing every VPS on the internet receives.
It is also frequently installed, never verified, and quietly doing nothing, which is worse than not having it, because you believe you are covered.
What it does and does not do
It does stop brute-force guessing from a single address, and it dramatically reduces the noise in your logs.
It does not stop a distributed attack using thousands of addresses, each trying once. It does not stop an attacker who has the correct password. And it does nothing about a vulnerability in your application.
Fail2ban raises the cost of guessing. It is not a substitute for key-based authentication, which removes the guessing route entirely.
Install and start with SSH
Install from your distribution's repositories. It arrives with the SSH jail enabled on most systems, which is the one that matters most.
Never edit jail.conf. It is replaced on upgrade, and your configuration disappears with it. Create jail.local instead: values there override the defaults and survive updates.
[sshd] enabled = true maxretry = 5 findtime = 600 bantime = 3600
Five failures within ten minutes earns an hour's ban. That is a reasonable starting point: harsh enough to stop automated guessing, forgiving enough that a colleague mistyping a password is inconvenienced rather than locked out for a day.
Whitelist yourself first
Before enabling anything, add your own address:
ignoreip = 127.0.0.1/8 203.0.113.45
Locking yourself out of your own server is the most common fail2ban experience, and on a VPS with no console access it is a genuine problem in place of an inconvenience.
If your address is dynamic, whitelisting is unreliable, so keep a second way in: a console through your provider's control panel, or a key-based login on a different port that fail2ban is not watching.
Verify it is actually working
This is the step that separates a working installation from a decorative one.
sudo fail2ban-client status sudo fail2ban-client status sshd
The second command shows failures detected and addresses currently banned. On a server that has been up for a day, both numbers should be greater than zero. The internet is constantly trying.
Zero failures detected means it is not reading your logs. That is the usual failure, and it has one usual cause: on systems using the systemd journal, the jail must be told to read from there rather than from a log file that no longer exists.
An installation showing zero after a day is not a quiet internet. It is a broken configuration.
Other jails worth enabling
SSH is the default and not the only target.
Mail. If the server handles mail, authentication attempts against it are constant and worth banning.
The web server, for repeated 404s from scanners probing for known vulnerable paths.
The application. WordPress login attempts can be banned if failures are logged somewhere fail2ban can read, which usually needs a plugin that writes them.
Enable them one at a time and check each is detecting. A jail pointed at the wrong log file is silently inactive, and enabling six at once means not knowing which of them work.
Ban times
A long ban feels safer and mostly punishes your own mistakes. Automated attackers move on quickly; the person locked out for a day is usually a legitimate user.
An hour handles most of it. For repeat offenders, incremental banning increases the duration each time the same address returns, which targets persistence instead of a single bad afternoon.
Permanent bans accumulate into a firewall rule list that grows without limit. Avoid them unless you are dealing with something specific.
Bans do not survive a restart by default
Firewall rules created by fail2ban are lost when the service or the machine restarts, and the ban list starts empty.
That is usually fine. A returning attacker is re-banned within minutes. It matters if you were relying on long bans to hold something out.
Unbanning
sudo fail2ban-client set sshd unbanip 203.0.113.45
Useful when a client is locked out. Add them to the whitelist afterwards if it is going to keep happening: unbanning the same person weekly is a sign the configuration is wrong for them rather than that they are careless.
Where it fits
Fail2ban is one layer, and the layer beneath it matters more.
Disable password authentication for SSH entirely and use keys: an attacker cannot guess a key, so the jail becomes a way of keeping logs quiet instead of a defence you depend on.
Then a firewall that only opens the ports you need, and a system that installs security updates. Securing your VPS walks through the full set, and fail2ban is worth having after those rather than instead of them.
Check it again in a month
Run the status command occasionally. A jail that was working can stop. A log path changed by a package update, a service renamed, a configuration edit that did not take.
The failure is always silent, because a security tool that is not blocking anything looks exactly like a quiet week.
Before changing any rules, it is worth arranging a way back in. How to Configure a Firewall Without Locking Yourself Out goes into the habit that turns a lockout into a five-minute wait.
Read what is actually being banned
The tool works silently, and reading its record occasionally is what tells you whether it is doing anything useful.
fail2ban-client status
fail2ban-client status sshd 2>/dev/null | tail -5
zgrep -h 'Ban ' /var/log/fail2ban.log* 2>/dev/null | awk '{print $NF}' | sort | uniq -c | sort -rn | head
A jail with no bans is either working perfectly or not matching anything, and those look identical from the outside. Comparing the ban count against the failure count in the underlying log distinguishes them.
An address banned repeatedly is a candidate for a permanent block, since the temporary one is being served rather than deterring anything.
Watch for the ban that catches your own systems
The failure people notice is a legitimate system being blocked, and it looks like an intermittent fault.
fail2ban-client status sshd 2>/dev/null | grep -i 'banned IP' curl -s ifconfig.me; echo
A monitoring check with an outdated credential, a backup job whose key was rotated, or a mail client with a wrong password will each generate repeated failures and be banned.
The result is a service that stops working for a period and then resumes, with nothing in its own logs explaining why. Adding your own systems to the exempt list before this happens is the cheaper order.