Secure SSH with Fail2ban and Key-Only Logins on Ubuntu

Harden the most exposed part of your VPS by switching SSH to keys only, tightening the daemon config, and automatically banning repeated login abuse.

Ubuntu hardeningSSH securityFree and open-source
What you learn

How to confirm SSH key access, disable password logins safely, add a small set of safer SSH defaults, and configure Fail2ban without locking yourself out.

Best for

New VPS owners, self-hosters, and anyone who still sees constant SSH password probes in logs.

Risk to watch

Changing SSH settings before key login is tested can cut off your only access. Verify first, then harden.

Before you begin

  • An Ubuntu server you can already reach over SSH.
  • A non-root sudo user on the server.
  • An SSH keypair on your local machine.
  • Access to your VPS provider console in case you need recovery.

SSH is the front door to most Linux servers. That makes it one of the first things attackers scan, brute-force, and abuse. The goal here is not perfect security. It is a clean, practical baseline that removes easy wins for attackers and lowers noise in your logs.

Why this hardening step matters

Password-based SSH logins are easy to understand, but they are also easy to target. Bots constantly try common usernames and weak passwords on internet-facing servers. Key-only login is far stronger because the private key stays on your device and is not something a bot can guess. Fail2ban adds another layer by watching authentication logs and temporarily banning IPs that keep failing.

Expected outcome: Your server still accepts your SSH login, password authentication is disabled, obvious brute-force attempts are blocked faster, and your SSH logs become much quieter.

Step 1: Verify SSH key access before changing anything

On your local machine, generate a key if you do not already have one:

ssh-keygen -t ed25519 -C "your-name@your-device"

Copy the public key to the server:

ssh-copy-id youruser@your-server-ip

If ssh-copy-id is unavailable, copy it manually:

cat ~/.ssh/id_ed25519.pub
ssh youruser@your-server-ip
mkdir -p ~/.ssh
chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

Open a second terminal and test a new login before touching the SSH config:

ssh youruser@your-server-ip

If the new login works without asking for the server account password, you are ready. Keep your original session open during the whole process so you have a recovery shell if a change goes wrong.

Step 2: Tighten the SSH daemon settings

Back up the existing SSH server config:

sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak

Edit the config file:

sudo nano /etc/ssh/sshd_config

Make sure these settings exist and are not duplicated in conflicting ways:

PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
ChallengeResponseAuthentication no
PubkeyAuthentication yes
UsePAM yes
X11Forwarding no
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2

If you changed the SSH port previously, keep that setting consistent with your firewall and Fail2ban config. Before restarting, validate the syntax:

sudo sshd -t

If the test returns no errors, reload SSH:

sudo systemctl reload ssh

Open another fresh terminal and confirm that key login still works. Do not close your original session until this test succeeds.

Step 3: Allow SSH through UFW and enable the firewall

If UFW is not already in use, add the OpenSSH rule first:

sudo ufw allow OpenSSH
sudo ufw enable
sudo ufw status verbose

If you use a custom SSH port, allow that exact port instead:

sudo ufw allow 2222/tcp

Do not enable UFW until the matching SSH rule is in place, or you can block your own connection.

Step 4: Install and tune Fail2ban

Install the package:

sudo apt update
sudo apt install fail2ban -y

Create a local jail config instead of editing the package defaults:

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local

Find the [sshd] section and make sure it is enabled. A practical starting point looks like this:

[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
backend = systemd
maxretry = 5
findtime = 10m
bantime = 1h

If you use a custom SSH port, change port = ssh to that port number. Then restart and check status:

sudo systemctl enable --now fail2ban
sudo systemctl restart fail2ban
sudo fail2ban-client status
sudo fail2ban-client status sshd

You should see the sshd jail listed and active. If repeated failures come from the same IP, Fail2ban will add a temporary ban.

Expected outcome and verification

Run these checks after setup:

ssh youruser@your-server-ip
sudo ss -tulpn | grep ssh
sudo systemctl status ssh --no-pager
sudo systemctl status fail2ban --no-pager
sudo fail2ban-client status sshd
sudo journalctl -u ssh -n 50 --no-pager

A healthy result usually means:

  • Your SSH login works with a key.
  • Password login attempts are rejected.
  • Fail2ban shows the sshd jail as active.
  • The firewall allows only the SSH access you intended.

Troubleshooting common problems

You cannot log in after disabling passwords.
Use the VPS console, restore /etc/ssh/sshd_config.bak, and confirm your public key is in the correct user account’s authorized_keys file.

sudo sshd -t shows a config error.
Check for duplicate directives, spelling mistakes, or settings placed inside the wrong block.

Fail2ban says the SSH jail is inactive.
Confirm the log backend matches your system. On modern Ubuntu, backend = systemd is often the right choice.

You locked yourself out with UFW.
Use your provider console or rescue mode, add the SSH allow rule, and only then re-enable the firewall.

Warning: Avoid “hardening everything at once.” Safe SSH security comes from verifying each change in order, not from copying a giant config block and hoping for the best.

What to do next

Once SSH access is hardened, the next easy win is keeping security patches moving automatically. Continue with Set Up Automatic Security Updates with unattended-upgrades.