SecurityWritten by GLWS15 min readUbuntu 24.04 LTS / AlmaLinux 9
Commands run on your server unless a section says otherwise. Replace example usernames, addresses and paths with your own values. Keep a working session open when changing remote access.

What you need first

Complete the SSH key and sudo guides first. You must be able to log in with a key as a non-root account and run sudo before you disable anything. If you cannot, this guide will lock you out.

Keep your current SSH session open throughout, and make sure you can reach your provider's recovery console. Changes below are tested before they are applied and are confirmed from a second terminal.

On the VPS: confirm the current effective SSH settings
sudo sshd -T | grep -Ei "^(passwordauthentication|permitrootlogin|pubkeyauthentication|kbdinteractiveauthentication|port) "

Write a hardening drop-in file

Modern OpenSSH reads files in /etc/ssh/sshd_config.d/ at the top of the main configuration. OpenSSH uses the first value it sees for each setting, so name your file with a low number such as 00-hardening.conf. Provider images often include a later file, such as 50-cloud-init.conf, that turns password login back on, and the earlier file wins.

This file disables password and keyboard-interactive login, forbids direct root login and limits attempts per connection.

Both systems
sudo tee /etc/ssh/sshd_config.d/00-hardening.conf >/dev/null <<'EOF'
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
MaxAuthTries 3
LoginGraceTime 30
EOF
sudo sshd -t && echo "Configuration OK"
Leave the SSH port alone unless you have a reason. On Ubuntu 24.04 the port can be controlled by a systemd socket, so changing it takes extra steps, and it does not add real security.

Check what OpenSSH will actually use

The sshd -T command prints the effective configuration after reading every file. Check that each setting shows the value you expect. If passwordauthentication still says yes, another file is overriding yours; find it with grep.

Both systems
sudo sshd -T | grep -Ei "^(passwordauthentication|permitrootlogin|kbdinteractiveauthentication|maxauthtries) "
sudo grep -ri "PasswordAuthentication" /etc/ssh/sshd_config /etc/ssh/sshd_config.d/

Reload and test from a second terminal

Reloading applies the configuration without ending existing sessions. The service is called ssh on Ubuntu and sshd on AlmaLinux. Then open a new terminal on your own computer and log in with your key. Also confirm that a password login is refused.

Ubuntu
sudo systemctl reload ssh
AlmaLinux
sudo systemctl reload sshd
On your computer: the key login should work, the password attempt should fail
ssh deploy@YOUR_SERVER
ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password deploy@YOUR_SERVER

Install Fail2ban

Fail2ban watches logs and temporarily bans addresses that repeatedly fail to log in. With password login off it mostly reduces log noise and load from bots, which still matters on a small VPS. On Ubuntu it is in the main archive. On AlmaLinux it comes from the EPEL repository, and the fail2ban-firewalld package connects it to firewalld.

Ubuntu only
sudo apt update
sudo apt install fail2ban
AlmaLinux only
sudo dnf install epel-release
sudo dnf install fail2ban fail2ban-firewalld

Configure the SSH jail

Never edit jail.conf, which package updates replace. Create /etc/fail2ban/jail.d/sshd-local.conf instead. This bans an address for one hour after five failures within ten minutes and reads from the systemd journal, where both distributions log SSH. Add your own fixed IP address to ignoreip if you have one, so a typing mistake cannot ban you.

Fail2ban must ban the port actually used by SSH. The default ssh service name means port 22; the commands below read the effective configured port instead.

Both systems
SSH_PORT=$(sudo /usr/sbin/sshd -T | awk '/^port /{print $2;exit}')
sudo tee /etc/fail2ban/jail.d/sshd-local.conf >/dev/null <<EOF
[sshd]
port = $SSH_PORT
enabled = true
backend = systemd
maxretry = 5
findtime = 10m
bantime = 1h
# ignoreip = 127.0.0.1/8 ::1 YOUR_HOME_IP
EOF
sudo systemctl enable --now fail2ban
sudo fail2ban-client reload
Check the jail
sudo fail2ban-client status
sudo fail2ban-client status sshd

Unban yourself and review bans

If you ban your own address, use the recovery console or another network, then remove the ban. List the failures that led to it in the journal. Keep bantime modest at first; repeated offenders can be handled later with the recidive jail.

Unban an address
sudo fail2ban-client set sshd unbanip 203.0.113.50
Recent SSH failures
sudo journalctl -u ssh -u sshd --since "1 hour ago" --no-pager | grep -i "failed" | tail
Fail2ban does not replace keys, updates or a firewall. It is one layer.

Official documentation

These upstream references provide more detail on the commands and workflows in this guide.