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
- A Linux server you can reach over SSH (Debian, Ubuntu, Rocky, AlmaLinux, anything modern).
- A user account with
sudorights. - Five minutes, and your own home or office IP address handy. You'll want that so you don't ban yourself.
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.
| Setting | What it does |
|---|---|
maxretry | How many failures are allowed before a ban. |
findtime | The window those failures must happen in, in seconds. |
bantime | How long the ban lasts, in seconds. Use -1 for permanent. |
ignoreip | IPs 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.
- Use a server with a static IP or a small VPS as a jump box, and add that fixed IP to
ignoreip. - If your provider gives you an out of band console (most VPS panels do, including the Bytte.cloud panel), you can always log in there and run the unban command even when SSH is blocked.
- Keep
bantimereasonable. A 24 hour ban on yourself is annoying. A permanent one is a problem.
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.
- Update the
portline in your Fail2banjail.localto match. - Open the new port in your firewall before you disconnect.
- Connect with
ssh -p 2222 [email protected]from now on.
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.



