Get your free server today! View Plans →
Home Plans Blog About Contact Panel Join Discord
Security

How to protect SSH with Fail2ban

Bots hammer port 22 all day long. Here's how to set up Fail2ban and a few sshd tweaks that ban brute force attackers and keep you from locking yourself out.

How to protect SSH with Fail2ban

If you put a Linux server on the internet, bots will start guessing your SSH password within minutes. This tutorial walks you through installing Fail2ban, writing a sensible SSH jail, and adding a few sshd settings that make brute force attacks pointless. It's written for anyone who runs their own VPS or game server box and wants to sleep better at night.

Why SSH gets attacked all day

Port 22 is the front door to most Linux servers, and everyone knows it. Automated scanners crawl the entire IPv4 space looking for anything listening on that port. When they find one, they start trying common usernames (root, admin, ubuntu, pi) with lists of leaked passwords. This isn't personal. It's just background noise on the internet, and it never stops.

If you tail your auth log on a fresh server, you'll usually see it within an hour. Here's the kind of thing you'll find.

Failed password for root from 193.32.162.45 port 41122 ssh2
Failed password for invalid user admin from 61.177.173.18 port 52344 ssh2
Failed password for invalid user test from 218.92.0.107 port 33890 ssh2

Fail2ban watches that log. When one IP racks up too many failures in a short window, Fail2ban tells your firewall to drop that IP for a while. The attacker moves on, your CPU stays quiet, and your log gets a lot shorter.

What you need before you start

You can find your current public IP by running this from your own computer, not the server.

curl ifconfig.me

Write that number down. We'll use it in a moment.

Step 1: Install Fail2ban

On Debian and Ubuntu, Fail2ban is in the default repositories.

sudo apt update
sudo apt install fail2ban -y

On Rocky, AlmaLinux, or other RHEL based systems, it lives in EPEL.

sudo dnf install epel-release -y
sudo dnf install fail2ban -y

Once it's installed, enable the service so it starts on boot, and start it now.

sudo systemctl enable fail2ban
sudo systemctl start fail2ban

Check that it's alive.

sudo systemctl status fail2ban

You want to see active (running) in green. If it failed to start, jump to the troubleshooting section near the end. Most early problems come from a config typo, and there's an easy way to catch those.

Step 2: Understand jail.local before you touch it

Fail2ban ships with a file called /etc/fail2ban/jail.conf. Do not edit that one. Package updates can overwrite it, and you'll lose your changes. Instead you create a file called jail.local in the same folder. Fail2ban reads jail.conf first, then reads jail.local on top of it, so anything you put in jail.local wins. Your settings survive updates that way.

A "jail" is just a named set of rules. Each jail points at a log file, uses a filter to spot failures, and has thresholds that decide when to ban. The one we care about is the SSH jail. Here are the four settings you'll set most often.

SettingWhat it does
maxretryHow many failures are allowed before a ban.
findtimeThe window those failures must happen in, in seconds.
bantimeHow long the ban lasts, in seconds. Use -1 for permanent.
ignoreipIPs that are never banned, no matter what.

So if maxretry is 5 and findtime is 600, that means five failed logins inside ten minutes earns a ban.

Step 3: Write your SSH jail

Open the file with your editor of choice.

sudo nano /etc/fail2ban/jail.local

Paste in the following. Replace the example IPs in ignoreip with your real home IP from earlier. You can list more than one, separated by spaces.

[DEFAULT]
# Never ban these. localhost plus your own IP.
ignoreip = 127.0.0.1/8::1 203.0.113.42

# Ban for one hour by default.
bantime  = 3600
findtime = 600
maxretry = 5

[sshd]
enabled  = true
port     = ssh
maxretry = 4
bantime  = 86400

A few notes on what's happening there. The [DEFAULT] block sets values for every jail. The [sshd] block then overrides a couple of them just for SSH, because SSH is the part bots hammer hardest. We drop maxretry to 4 and stretch bantime to 86400 seconds, which is a full day. So a noisy attacker disappears for 24 hours after four bad tries.

The port = ssh line uses the name ssh, which maps to port 22 by default. If you change your SSH port later (we'll do that in Step 7), come back and set this to the real number, for example port = 2222.

One important detail about the log source. On modern Debian and Ubuntu, login attempts often go to the systemd journal rather than a plain text file. If your distro uses the journal, add this line inside the [sshd] block.

backend = systemd

If you skip that on a journal based system, the jail will look at an empty or missing /var/log/auth.log and never ban anyone. We've been bitten by this on fresh Ubuntu installs, so it's worth checking.

Save and close. In nano that's Ctrl+O, Enter, then Ctrl+X.

Step 4: Test the config, then reload

Before you restart anything, ask Fail2ban to read your config and report problems. This catches typos and bad values without taking the service down.

sudo fail2ban-client -t

If you see OK or no errors, you're good. Now reload so your jail goes live.

sudo systemctl restart fail2ban

Confirm the SSH jail is actually running.

sudo fail2ban-client status

You should see sshd listed under the jail list, like this.

Status
|- Number of jail:      1
`- Jail list:   sshd

If sshd is missing from that list, the jail didn't enable. The troubleshooting section covers why.

Step 5: Watch the bans roll in

Give it a little time, then check the SSH jail directly. This is the command you'll use most.

sudo fail2ban-client status sshd

The output tells you how many attempts it has seen and who's currently locked out.

Status for the jail: sshd
|- Filter
|  |- Currently failed: 2
|  |- Total failed:     341
|  `- File list:        /var/log/auth.log
`- Actions
   |- Currently banned: 6
   |- Total banned:     58
   `- Banned IP list:   61.177.173.18 218.92.0.107 193.32.162.45...

That Total banned number climbs fast on a public server. Each of those IPs is a bot that gave up because Fail2ban shut the door.

If you ever need to let someone back in (maybe a coworker fat fingered their password a few times), you can unban an address by hand.

sudo fail2ban-client set sshd unbanip 198.51.100.7

And to test that banning works, you can ban an IP manually too. Don't ban your own.

sudo fail2ban-client set sshd banip 198.51.100.7

Step 6: Make sure you can't lock yourself out

This is the part people skip and then regret. The ignoreip line from Step 3 is your safety net. As long as your real IP is listed there, Fail2ban will never ban you, even if you mistype your password ten times in a row.

But home connections often have a dynamic IP that changes when your router reconnects. If that happens and you get locked out, you have a few options.

The console access point is the big one. As long as you have a way into the box that doesn't go through SSH, a lockout is a minor inconvenience instead of a disaster.

Step 7: Harden sshd so brute force barely matters

Fail2ban is a great backstop, but the real win is making password guessing impossible in the first place. A few changes to your SSH server config do most of the work. Open it up.

sudo nano /etc/ssh/sshd_config

Find these settings, uncomment them, and set them like so. If a line isn't there, add it.

# Stop direct root logins.
PermitRootLogin no

# Turn off password logins entirely. Keys only.
PasswordAuthentication no

# Move SSH off the default port to cut the noise.
Port 2222

# Don't allow empty passwords, ever.
PermitEmptyPasswords no

A word of warning on PasswordAuthentication no. Only set this once you've confirmed you can log in with an SSH key. If you turn off passwords without a working key, you'll lock yourself out completely. So set up your key first.

If you've never done that, generate a key on your own machine and copy it up.

ssh-keygen -t ed25519 -C "[email protected]"
ssh-copy-id -p 22 [email protected]

Then test that key login works in a brand new terminal before you change anything else. Keep your current session open as a fallback while you test.

Changing the port to 2222 (or any unused high number) won't stop a determined attacker, but it cuts the dumb automated traffic dramatically, because most scanners only check port 22. If you do change the port, remember three things.

If you use UFW, opening the port looks like this.

sudo ufw allow 2222/tcp
sudo ufw reload

Now check your sshd config for mistakes before restarting it. This is the SSH equivalent of the Fail2ban test command, and it can save you from a lockout.

sudo sshd -t

If that prints nothing, the config is valid. Restart SSH to apply it.

sudo systemctl restart sshd

Do not close your current session yet. Open a second terminal and connect on the new port with your key. If it works, you're done. If it doesn't, you still have your first session open to fix things.

Step 8: Read the auth log when you want detail

Sometimes you want to see exactly what's hitting your server. The raw log tells you more than the Fail2ban summary. On Debian and Ubuntu, SSH events land in /var/log/auth.log.

sudo tail -f /var/log/auth.log

On RHEL family systems it's a different file.

sudo tail -f /var/log/secure

And if your distro logs to the journal, read it there instead.

sudo journalctl -u ssh -f

You can also grep for Fail2ban's own actions to see a tidy history of bans.

sudo grep "Ban" /var/log/fail2ban.log

That gives you a line per ban, with the IP and timestamp, which is handy if you're trying to figure out whether an attack is ramping up.

Troubleshooting

I locked myself out

Don't panic. If you have console access through your host's panel, log in there and run sudo fail2ban-client set sshd unbanip YOUR.IP. If the ban was because of a config change instead, you can stop Fail2ban entirely from the console with sudo systemctl stop fail2ban, fix the issue, then start it again. Adding your IP to ignoreip prevents a repeat.

The sshd jail is not active

Run sudo fail2ban-client status and check the jail list. If sshd isn't there, the usual cause is enabled = true being missing from the [sshd] block, or a syntax error elsewhere in the file. Run sudo fail2ban-client -t to find the bad line, fix it, and restart.

Fail2ban runs but never bans anyone

This is almost always the log source. If your system uses systemd journald, Fail2ban may be reading an empty /var/log/auth.log. Add backend = systemd to your [sshd] jail and restart. You can confirm what it's reading by looking at the File list line in fail2ban-client status sshd.

Fail2ban will not start at all

Read the service errors with sudo journalctl -u fail2ban -n 50. A missing log file is a common cause. On a minimal install, rsyslog may not be present, so /var/log/auth.log never gets created. Either install rsyslog or switch the backend to systemd as above.

I changed the SSH port and now Fail2ban ignores attacks on it

Fail2ban needs to know the real port for its banning to apply cleanly. Set port = 2222 (your actual port) inside the [sshd] block, not the word ssh, then restart. The filter still reads the same log, but the firewall rule now targets the right port.

My key login fails after hardening

Run the client in verbose mode to see why: ssh -v -p 2222 [email protected]. Common causes are wrong file permissions on the server's ~/.ssh folder (it should be 700) or authorized_keys (it should be 600). Fix those with chmod 700 ~/.ssh and chmod 600 ~/.ssh/authorized_keys.

Wrapping up

You now have Fail2ban watching your SSH log and banning anything that knocks too many times, plus an sshd config that turns off root logins and passwords entirely. Together those two things take SSH brute force from a daily annoyance to a non issue. The key habit to keep is the safety net: always have console access and your own IP in ignoreip so a bad ban never traps you. Check fail2ban-client status sshd every now and then, watch that ban count climb, and enjoy the quiet.

Common questions

Does Fail2ban replace good SSH passwords or keys?

No. Fail2ban slows attackers down by banning IPs after repeated failures, but the real protection is key based login with passwords turned off. Use both together for the strongest setup.

How do I unban an IP that got blocked by mistake?

Run sudo fail2ban-client set sshd unbanip followed by the address, for example sudo fail2ban-client set sshd unbanip 198.51.100.7. The IP can connect again right away.

Why is Fail2ban running but never banning anyone?

Almost always the log source. On systemd based distros the SSH events go to the journal, not /var/log/auth.log. Add backend = systemd to your sshd jail and restart Fail2ban.

Will Fail2ban ever lock me out of my own server?

It can if your IP isn't in the ignoreip list. Add your home or office IP there, and keep console access through your host panel as a backup so a bad ban is never permanent.

Should I change my SSH port to stop attacks?

Moving off port 22 cuts most automated scanning traffic, but it isn't real security on its own. Pair it with key only logins and Fail2ban, and update the port line in your jail.local to match.

SA
Sofia Almeida
Systems Engineer at Bytte.cloud

Part of the Bytte.cloud team. We run game servers, bots and websites for a living, and we write these guides from what we see day to day in support and on our own servers.

Want to try this on real hardware?

Bytte.cloud has free plans for game servers, bots and websites. No credit card, set up in seconds.

Start for free See the plans