Blog
SavedSearch
Visit Hostlelo

Protect Your VPS from SSH Brute Force with fail2ban

Install Fail2ban on Ubuntu or Debian, enable and inspect the SSH jail, distinguish a manual ban from an end-to-end test, and keep a recovery path.

The short answer

Install Fail2ban from your distribution, enable the sshd jail in jail.local, set maxretry, findtime and bantime explicitly, and verify the effective running values with fail2ban-client. Keep a working console path and SSH session while you test; Fail2ban reduces guessing but does not replace key authentication.

Repeated login dots stop at a shield and padlock in front of a server, illustrating fail2ban SSH protection.
Reading options

On this page

Fail2ban can reduce repeated SSH password guessing by temporarily blocking sources that cross a configured failure threshold. This guide installs it, enables the sshd jail in jail.local, and explicitly sets five failures within ten minutes to a one-hour ban. Do not assume those values are universal defaults: distribution files, panel integrations and existing .local overrides can change the effective jail.

Upstream OpenSSH documents PasswordAuthentication yes as its default, but a distribution, cloud image or hosting provider can override it. Check the effective server setting before you describe your own exposure: sudo sshd -T | grep -i '^passwordauthentication'. Fail2ban still does not make password authentication as strong as a well-managed SSH key.

By the end of this guide you will have fail2ban installed, the SSH jail enabled and tested, and you will know exactly how to unban yourself if you trip it by accident.

What fail2ban actually is

fail2ban is a small program that reads your log files, looks for patterns like "failed password", and tells your firewall to block the IP address behind them for a while. A "jail" is one rule set: which log to watch, which pattern counts as a failure, and what to do about it.

Think of it like a doorman. He does not change the lock. He just notices the person who tried the wrong key five times and asks them to wait outside for a while.

Why this matters: fail2ban does not make a weak password strong. It slows attackers down and cuts the noise in your logs. The real fix for SSH is key login, which I cover in setting up SSH key login. Use both together.

Before you start

Run through this list first. It saves you from the classic "I locked myself out" evening.

  • A VPS running Ubuntu or Debian, with root or sudo access over SSH.
  • Your provider's web console (sometimes called VNC or serial console) login. This is your back door if SSH stops answering you.
  • Your own public IP address. Search "what is my IP" in a browser, or ask your router. In this guide I use 203.0.113.25 as the example.
  • A second device on a different network for the test ban, like a phone on mobile data with an SSH app. Optional, but nice.
  • About 20 to 30 minutes.
  • Risk level: low to medium. The main risk is banning yourself, and every step below has a way out.

Keep your current SSH session open, but do not rely on it surviving a ban: firewall actions and rule order can also interrupt established connections. Verify that your provider console works before changing rules, keep your administration IP out of the test, and open a second terminal for checking.

If you are still picking a server, my guide on choosing a VPS plan for your workload helps. fail2ban itself is light, so it runs fine on the smallest plans.

Set up fail2ban for SSH, step by step

All commands below run on the VPS over SSH. If you are logged in as root, you can drop the sudo part.

Step 1: Update the package list and install fail2ban

This updates your package lists and installs fail2ban from your distribution's own repository.

sudo apt update
sudo apt install fail2ban

The official fail2ban install notes for Debian and Ubuntu say the service starts automatically after install. You should see apt finish without errors.

This prints the installed version, which proves the client works.

sudo fail2ban-client version

At the time of writing (16 October 2026), the newest upstream release on GitHub is fail2ban 1.1.1, released on 15 August 2026. Your distribution may ship an older version. For example, Debian 12 (bookworm) packages 1.0.2. That is fine for this guide, because the settings we use exist in both.

Quick tip: the fail2ban project says to always use fail2ban-client to control it. Do not start fail2ban-server by hand.

Step 2: Check which port SSH really uses

fail2ban needs to block the port SSH actually listens on. The OpenSSH default is port 22, but some providers or past you may have changed it.

This asks the SSH server to print its effective settings, filtered to the port line.

sudo sshd -T | grep -i '^port'

You should see something like port 22. If you see another number, such as port 2222, write it down. You will need it in Step 4.

Why this matters: if fail2ban bans port 22 but SSH listens on 2222, the attacker is "banned" from a door they never used. Nothing breaks, but nothing is protected either.

Step 3: Back up and look at the default config

fail2ban keeps its defaults in /etc/fail2ban/jail.conf. The top of that file says, in capital letters, "YOU SHOULD NOT MODIFY THIS FILE." The reason is that package updates can overwrite it. Your own changes go in jail.local instead.

This illustrative block creates a unique root-only directory outside web roots, copies the current configuration into it, and prints the exact path you must record. It has not been executed on your server.

(
  set -eu
  backup_dir="$(sudo mktemp -d /root/fail2ban-backup.XXXXXXXX)"
  sudo chmod 700 "$backup_dir"
  sudo cp -a -- /etc/fail2ban/. "$backup_dir/"
  sudo test -f "$backup_dir/jail.conf"
  printf 'Fail2ban backup: %s\n' "$backup_dir"
)

Keep the printed path with the change record and protect an off-server copy. This backup covers Fail2ban configuration only, not firewall state or the rest of the VPS. Do not guess the generated directory name during rollback.

If a jail.local already exists (some providers ship one), read it before you change anything.

sudo cat /etc/fail2ban/jail.local

If the file does not exist, good. You will create it in the next step.

Heads up: do not copy all of jail.conf into jail.local. The fail2ban manual says .local files should contain only the settings you want to change. A full copy freezes old defaults and makes updates confusing.

Step 4: Create jail.local and enable the sshd jail

The upstream jail.conf checked for this review contains these base values. Your installed package can add distribution files, and later .local files can override them, so the configuration below sets the values explicitly:

  • bantime = 10m: how long a ban lasts.
  • findtime = 10m: the time window fail2ban counts failures in.
  • maxretry = 5: how many failures in that window trigger a ban.
  • ignoreip is commented out: no extra addresses are protected from bans.
  • enabled = false: every jail is off until you turn it on.

The [sshd] section in jail.conf already sets port = ssh and points to the right log with logpath = %(sshd_log)s and backend = %(sshd_backend)s. You only need to switch it on.

Open a new file in the nano editor.

sudo nano /etc/fail2ban/jail.local

This is a new-install example. If jail.local already exists, merge only the intended settings and preserve other jails and required ignoreip entries rather than replacing the file. The sample protects localhost and your administration IP (replace 203.0.113.25), explicitly sets the timers and threshold, and enables sshd. DEFAULT values affect other jails too, so review that scope.

[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 203.0.113.25
bantime = 1h
findtime = 10m
maxretry = 5

[sshd]
enabled = true
port = ssh

Save with Ctrl+O, press Enter, then exit with Ctrl+X.

The [sshd] plus enabled = true pattern comes straight from the example in the comments of the official jail.conf. Still, test it on a test VPS first if you have one, especially if your provider added its own files under /etc/fail2ban/jail.d/.

If SSH listens on a custom port from Step 2, change the port line to that number. This version is for a server on port 2222.

[sshd]
enabled = true
port = 2222

A few "but what if" answers:

  • What if my home IP changes? Then ignoreip will not match anymore. That is fine, it just means you are treated like anyone else. Key login protects you there, and you can always unban yourself (Step 8).
  • What if I have more than one IP to protect? Add them on the same line, separated by spaces. CIDR ranges like 192.0.2.0/24 work too.

Quick tip: fail2ban also has an ignoreself setting that is true by default. It already ignores the server's own addresses, so you only need to list your remote IP.

Why the order matters: the manual says files in jail.d/*.conf are read before jail.local, and later settings win. So your jail.local overrides a provider's jail.d/defaults-debian.conf or similar file if both set the same thing.

Step 5: Test the configuration before reloading

This checks the config files for mistakes without changing the running service.

sudo fail2ban-client -t

If it reports an OK result, move on. If it prints an error, it usually names the file and line. Fix that first. A typo in a section name like [ssdh] is the most common one.

Step 6: Reload fail2ban and check the jail

This tells fail2ban to reread its configuration without a full restart.

sudo fail2ban-client reload

Now check the overall status. This lists the active jails.

sudo fail2ban-client status

You should see sshd in the jail list. Then look at the SSH jail itself, which shows failure counts and banned IPs.

sudo fail2ban-client status sshd

Read the effective threshold and timers from the running jail instead of relying on a package default:

sudo fail2ban-client get sshd maxretry
sudo fail2ban-client get sshd findtime
sudo fail2ban-client get sshd bantime

With this guide's sample, expect 5, 600 and 3600 seconds respectively. If the values differ, inspect later files under /etc/fail2ban/jail.d/*.local before testing bans.

If fail2ban is not set to start at boot, these two commands start it now and enable it for reboots. The official install notes list them in their CentOS and Fedora sections, and they are standard systemd commands on Debian and Ubuntu too.

sudo systemctl start fail2ban
sudo systemctl enable fail2ban

Step 7: Do a safe test ban

Use two levels of checking. A manual test ban verifies that Fail2ban accepts a ban and shows it in the jail, but it does not prove that SSH failures are detected or that traffic is blocked. A controlled failed-login test from another network provides stronger end-to-end evidence. Start with the safer manual check.

Method A: manually ban a documentation IP. This avoids deliberately blocking a real client, but it still invokes the configured ban action and may initialize firewall rules. It checks command acceptance, not the packet path or failed-login detection.

sudo fail2ban-client set sshd banip 192.0.2.10

Now list the banned IPs in the jail. You should see 192.0.2.10.

sudo fail2ban-client get sshd banip

Remove it again so your list stays clean.

sudo fail2ban-client set sshd unbanip 192.0.2.10

Method B: a controlled failed-login test from a second network you are authorized to use. Keep working provider-console access and use neither your administration IP nor an address in ignoreip. After verifying the actual test IP is banned and new SSH connections from it fail, unban that same test IP with Step 8 and confirm access returns.

From the second device, try to log in with a wrong password or a made-up user name at least 5 times within 10 minutes. This example uses a user that does not exist on your server.

ssh nosuchuser@example.com

Replace example.com with your server's IP address or host name, and add -p 2222 if you use a custom port. After the fifth failure, new connection attempts from that device should hang or get refused.

Back in your still-open first session, check the jail.

sudo fail2ban-client status sshd

You should see the second device's IP under the banned list.

Made-up example: Sam runs a small shop site on a VPS. He tests from his phone on mobile data, fails five logins, and watches the phone's mobile data IP appear in the banned list. His laptop at home, listed in ignoreip, keeps working the whole time. That is exactly the result you want.

Step 8: Unban yourself (or anyone)

This removes one IP from the SSH jail. Run it from your open session or the provider's web console.

sudo fail2ban-client set sshd unbanip 203.0.113.25

If you are not sure which jail banned an IP, this checks every jail for that address.

sudo fail2ban-client banned 203.0.113.25

And this removes the IP from all jails and from fail2ban's database in one go.

sudo fail2ban-client unban 203.0.113.25

Heads up: if you closed every SSH session and got banned, do not panic. Log in through your provider's web console, run the unban command above, and then add your IP to ignoreip. Without any action, a ban with our settings ends by itself after 1 hour.

Step 9: Optional, inspect UFW without enabling it

Fail2ban needs a compatible firewall action but does not require you to turn on UFW. The upstream Debian-family configuration can use nftables; your package or provider may differ. If UFW is already active, inspect its rules and confirm that the real SSH port and other required services remain reachable. Check which ban action the running jail actually uses before assuming that a manual ban affected traffic.

This read-only command shows whether UFW is active and its current rules. Do not enable a new firewall as a side effect of this Fail2ban guide: first inventory every needed inbound service, keep console access, and plan a separate tested firewall change with a rollback.

sudo ufw status verbose

How to check it worked

Run these checks once now, and again after your next reboot.

  1. Run sudo fail2ban-client ping. It tests whether the server is alive, and you should get a reply instead of an error.
  2. Run sudo fail2ban-client status and confirm sshd is in the jail list.
  3. Run sudo fail2ban-client status sshd and confirm the failed and banned counters are present. On a public server they usually start climbing soon.
  4. Look at the fail2ban log for ban events. The default log file is /var/log/fail2ban.log.

This shows the last 20 ban or unban lines from that log.

sudo grep -E "Ban|Unban" /var/log/fail2ban.log | tail -n 20

You should see lines that mention the sshd jail and an IP address. If the log is empty, wait a while or repeat Method B from Step 7.

Troubleshooting

"Failed to access socket path: /var/run/fail2ban/fail2ban.sock. Is fail2ban running?"

This message comes from fail2ban-client when it cannot find the server's socket file. In plain words: the fail2ban service is not running.

Likely cause: a config error stopped it from starting, or it never started. Run sudo fail2ban-client -t to find the broken line, fix it, then sudo systemctl start fail2ban.

The official install notes (in their CentOS section) mention an error about "no directory /var/run/fail2ban to contain the socket file". If you see that, create the folder with sudo mkdir /var/run/fail2ban and start the service again.

"Permission denied to socket: /var/run/fail2ban/fail2ban.sock, (you must be root)"

You ran fail2ban-client as a normal user. The fix is simple: put sudo in front of the command, or switch to root.

"Have not found any log file for 'sshd' jail."

fail2ban is looking for a log file such as /var/log/auth.log and it does not exist. Some newer systems only write SSH logs to the systemd journal, which is the system's built-in log store, and not to a text file.

Fix: tell the jail to read the journal. Add this line under [sshd] in your jail.local, then run sudo fail2ban-client -t and reload. Test this on a test VPS first.

[sshd]
enabled = true
port = ssh
backend = systemd

The current upstream Debian paths file already sets sshd_backend to systemd, so this matches what newer packages do anyway.

Symptom: sshd is not in the jail list

fail2ban-client status works, but sshd is missing, or status sshd gives an error about an unknown jail. This is a symptom description, not an exact message.

Check these, in this order:

  1. The file is named exactly /etc/fail2ban/jail.local, not jail.local.txt or saved in your home folder.
  2. The section header is exactly [sshd].
  3. The line reads enabled = true, not enable = true.
  4. You ran sudo fail2ban-client reload after saving.
  5. No file in /etc/fail2ban/jail.d/ sets enabled = false for sshd. Run sudo grep -rn enabled /etc/fail2ban/jail.d/ to check.

Symptom: the test ban from your phone never happens

You failed five logins from a second device, but the IP never shows up as banned. Again, this is a symptom, not an error message.

Likely causes: the test device's IP is inside a range in ignoreip, the failures were spread over more than 10 minutes, or the jail is watching the wrong port or log. Recheck Step 2 for the port, and the previous problem for the log backend. Also note that a device behind the same home router as your laptop shares its public IP, so it is ignored too.

Symptom: you banned yourself and every SSH attempt hangs

Your connection just sits there and times out. Use the provider's web console to log in, then run sudo fail2ban-client set sshd unbanip followed by your IP. Add your IP to ignoreip so it does not happen again, and consider switching to key login so you stop mistyping passwords.

Pro tips and when to ask for help

  • Use reload, not a full restart, for normal config changes. The man page also lists an --unban option, which only takes effect together with --restart (sudo fail2ban-client reload --restart --unban), handy when you want a clean slate.
  • Pair Fail2ban with SSH keys and consider disabling password authentication only after testing key access and recovery. Upstream PermitRootLogin defaults to prohibit-password, which disables password and keyboard-interactive authentication for root, not every other method. A distribution or local override can differ; check the effective configuration.
  • Upstream OpenSSH limits authentication attempts per connection with MaxAuthTries (default 6). LoginGraceTime (default 120 seconds) limits the time to authenticate; it is not an idle timeout for an already logged-in session. Local settings can differ. Fail2ban adds counting across connections.
  • If your server runs a control panel with its own firewall or brute-force tool, ask your host before adding fail2ban. Two tools fighting over the same firewall rules can cause odd bans.

The one message worth forwarding

If someone else manages your server, or you want a friend to double check your work, send this:

"Hi, could you please confirm fail2ban is protecting SSH on our VPS? I would like: 1) the sshd jail enabled in /etc/fail2ban/jail.local (not by editing jail.conf), 2) the jail port matching the real SSH port from sshd -T, 3) our office IP 203.0.113.25 in ignoreip, 4) the output of sudo fail2ban-client status sshd showing the jail is active. Please keep one SSH session open while testing so nobody gets locked out. Thanks!"

The takeaway: fail2ban is a doorman, not a lock, so turn it on in jail.local, test it from a second network, and keep SSH keys as your real protection.

Reader questions

Does fail2ban replace SSH keys?

No. fail2ban only slows down password guessing by banning IPs after repeated failures. SSH key login is the stronger protection, and the two work well together.

Should I edit jail.conf directly?

No. The file itself says you should not modify it because updates can overwrite it. Put only your changes in jail.local or a .local file under jail.d.

How long will fail2ban ban an IP?

This guide sets bantime = 1h. The upstream jail.conf checked for this review uses 10 minutes, but distribution files and local overrides can change the effective value. After the sshd jail starts, run sudo fail2ban-client get sshd bantime to read the active value in seconds.

Will fail2ban ban me if I mistype my password?

It can, if you fail maxretry times within findtime. Add your own IP to ignoreip to prevent that, and keep your provider's web console login handy as a backup.

Do I need ufw for fail2ban to work?

No. Fail2ban uses its configured firewall action. Do not enable UFW as a side effect of this guide. A new firewall needs a separate change plan covering every required inbound service, tested console access and rollback, not only an SSH allow rule.

Does fail2ban work if SSH runs on a custom port?

Yes, but you must set the port line in the [sshd] section of jail.local to that number. Check the real port with sudo sshd -T first.

Sources & further reading

  1. fail2ban releases on GitHub
  2. fail2ban README
  3. fail2ban default jail.conf
  4. fail2ban wiki: How to install fail2ban packages
  5. jail.conf(5) manual, Debian bookworm
  6. fail2ban-client(1) manual, Debian bookworm
  7. sshd_config(5) manual, Debian bookworm
  8. sshd(8) manual, Debian bookworm
  9. Ubuntu Server docs: Firewalls
  10. Debian package: fail2ban in bookworm
  11. jail.conf(5) manual, Debian testing
  12. fail2ban-client(1) manual, Debian testing

What changed

Clarified that jail defaults can be overridden, added effective-value checks, replaced the backup example with a unique private path, and added an original cover.

Originally published . About our editorial updates.

Your next project deserves a better foundation.

Explore hosting built for your next chapter.

Explore hosting

Find your next answer.

Search published guides and articles.

Ctrl / ⌘ K to open · Esc to close