How to protect a VPS from SSH brute force attacks

Protect a VPS from SSH brute force on Ubuntu or Debian: key-only login, Fail2ban, UFW rate limits, changing the port safely, and how not to lock yourself out.

Diagram: Fail2ban blocking IPs that guess SSH passwords before they reach the server

Key takeaways

  • Turning off SSH password login and using keys only is the single most effective defence against brute force.
  • Fail2ban bans IPs after a few failed logins, cutting log noise and load.
  • Changing the SSH port only reduces bot noise; on Ubuntu 24.04 it also needs a restart of ssh.socket.
  • Always keep your current SSH session open and test a new login before closing it.

The most effective way to protect a VPS from SSH brute force is to turn off password login entirely and allow SSH keys only: then bots can try millions of passwords and none will work. On top of that, add Fail2ban to ban IPs that keep failing, rate-limit connections in the firewall, and where possible allow SSH only from your own IPs. Changing the SSH port makes logs quieter, but it isn't a primary defence.

This guide walks through each step on Ubuntu 24.04 and Debian 12/13, in an order that won't lock you out of your own server, and shows what ZoPanel sets up and checks for you.

Is your VPS being brute-forced right now?

Almost certainly. Every public IP gets constant scans on port 22, often within minutes of the VPS being created. Take a look:

# Ubuntu and Debian both use the ssh.service unit
sudo journalctl -u ssh --since "24 hours ago" | grep -cE "Failed password|Invalid user"

Thousands a day is normal. Most of it is bots trying common usernames such as root, admin, ubuntu and test with weak passwords. They aren't targeting you specifically, but one weak password is enough to turn the server into a crypto miner or a spam relay.

Step 1: log in with an SSH key

On your own computer (Linux, macOS or Windows with OpenSSH):

ssh-keygen -t ed25519 -C "you@laptop"
ssh-copy-id root@203.0.113.10

Ed25519 keys are short, fast and the recommended choice for new keys. Give the private key a passphrase, so a lost laptop doesn't mean an instantly usable key.

If you have a hardware security key (a YubiKey or similar), you can create an ed25519-sk key, which requires a touch on the device for every login:

ssh-keygen -t ed25519-sk

Log in again with the key before moving to step 2. If the key doesn't work, stop and fix it: the next step turns passwords off.

It's also a good idea to work as a regular sudo user rather than as root:

adduser admin2
usermod -aG sudo admin2
mkdir -p /home/admin2/.ssh
cp /root/.ssh/authorized_keys /home/admin2/.ssh/
chown -R admin2:admin2 /home/admin2/.ssh
chmod 700 /home/admin2/.ssh && chmod 600 /home/admin2/.ssh/authorized_keys

Step 2: turn off password login

On Ubuntu 24.04 and Debian 12/13, /etc/ssh/sshd_config includes the files in /etc/ssh/sshd_config.d/ right at the top. For sshd, the first value read wins. Some cloud images ship a 50-cloud-init.conf that sets PasswordAuthentication yes, so give your file a name that sorts first:

sudo nano /etc/ssh/sshd_config.d/00-hardening.conf
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
MaxAuthTries 3
LoginGraceTime 30
  • PermitRootLogin prohibit-password lets root log in with a key but never with a password. If you created a sudo user in step 1, PermitRootLogin no is stricter still.
  • MaxAuthTries 3 drops the connection after three failed authentication attempts.
  • LoginGraceTime 30 gives each connection 30 seconds to log in.

Validate before reloading:

sudo sshd -t                                     # no output means the syntax is fine
sudo sshd -T | grep -E "passwordauthentication|permitrootlogin|maxauthtries"
sudo systemctl reload ssh

Keep your current SSH session open. Open a new terminal window and log in. Only close the old session once the new one works. If your provider has a web console (VNC), find out where it is before you start.

Step 3: install Fail2ban

With passwords off, brute force can't succeed, but bots still tie up connections and fill your logs. Fail2ban reads the logs and, when an IP fails too many times, bans it in the firewall for a while.

sudo apt install fail2ban

Don't edit jail.conf (it belongs to the package and can be overwritten on upgrade). Create /etc/fail2ban/jail.local:

[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
bantime.increment = true

[sshd]
enabled = true
backend = systemd

backend = systemd matters on Debian 12 and later: Debian no longer writes /var/log/auth.log by default, and the sshd jail won't start if it goes looking for that file. bantime.increment makes bans longer for repeat offenders.

sudo systemctl restart fail2ban
sudo fail2ban-client status sshd                     # currently banned IPs
sudo fail2ban-client set sshd unbanip 198.51.100.7   # unban if you banned yourself

If you work from a fixed IP, add ignoreip = 127.0.0.1/8 ::1 YOUR_IP under [DEFAULT] so a typo never bans you.

Since version 9.8, OpenSSH has PerSourcePenalties, on by default, which penalises addresses that repeatedly fail authentication (see the OpenSSH 9.8 release notes). Debian 13 ships OpenSSH 10.0, so it has this; Ubuntu 24.04 (OpenSSH 9.6) and Debian 12 (9.2) don't, so Fail2ban still earns its place there.

Step 4: rate-limit SSH in the firewall

UFW has a built-in limit rule: it denies an IP that opens 6 or more connections within 30 seconds.

sudo ufw limit 22/tcp
sudo ufw status

Stronger still is allowing only your own IPs on the SSH port, either in your provider's cloud firewall (security group) or in UFW:

sudo ufw allow from 198.51.100.7 to any port 22 proto tcp
sudo ufw delete limit 22/tcp

That removes almost all bot traffic, but it only suits a fixed IP or a VPN such as WireGuard. If your home IP changes, you'll need the provider's console to get back in.

Step 5: should you change the SSH port?

Moving SSH off port 22 cuts automated scanning sharply and makes logs easier to read. It does not stop a determined attacker, since scanning every port takes minutes. Treat it as noise reduction, done after keys and Fail2ban.

The safe order:

  1. Open the new port in the firewall first: sudo ufw allow 2222/tcp (and in the cloud firewall, if you use one).
  2. Add Port 2222 to 00-hardening.conf.
  3. Apply it:
    • Ubuntu 24.04: SSH is socket-activated, so the listening port comes from ssh.socket. Run sudo systemctl daemon-reload && sudo systemctl restart ssh.socket. Restarting ssh.service alone isn't enough.
    • Debian 12/13: sudo systemctl restart ssh.
  4. Check with sudo ss -tlnp | grep ssh, and log in from a new session with ssh -p 2222.
  5. Once you're sure, remove the port 22 firewall rule. The sshd jail bans on the ssh port (22) by default, so add port = 2222 to its [sshd] section.

Step 6: keep OpenSSH updated

Brute force isn't the only threat. In 2024 the regreSSHion flaw (CVE-2024-6387) in sshd affected portable OpenSSH 8.5p1 through 9.7p1 and could lead to remote code execution as root. Ubuntu and Debian patched it in their own packages, so the version number you see may still look old: check that package updates are applied rather than reading the version string.

sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades

The rest of VPS hardening (users, automatic updates, exposed services) is covered in how to secure an Ubuntu VPS.

How people lock themselves out

Mistake How to avoid it
Passwords turned off before the key works Test key login first and keep the old session open
A cloud-init file turns passwords back on Name your file 00-...conf and check with sshd -T
New port not open in the firewall Open the new port first, close the old one last
Ubuntu 24.04 still listening on 22 Restart ssh.socket after daemon-reload
Fail2ban bans your own IP Add ignoreip and know the unbanip command
SSH allowed from one IP, then the IP changes Use a VPN, or know how to reach your provider's console

Protecting a VPS from SSH brute force with ZoPanel

When you install ZoPanel, the installer does part of this for you, as described in the Security documentation:

  • UFW is turned on with your current SSH port plus 80, 443 and 8888 open, and enabled at boot. Manage rules under Security → Firewall.
  • Fail2ban gets an sshd jail (5 failures in 10 minutes, 1 hour ban) and a zopanel jail for panel password and 2FA failures (8 failures in 15 minutes, 1 hour ban). Banned IPs appear on the Security page, where you can unban them with one click.
  • Security Center scores the server, including SSH root password login, SSH password authentication, SSH login attempts and Fail2ban. The Fix buttons for SSH always check that a key is set up first, so a fix can't lock you out.

Hosting customers never get a root shell in ZoPanel. Their SFTP access is chrooted to their home directory, they can add SSH keys under My account → SFTP & SSH keys and turn on Keys only to disable password login for their account (see FTP and SFTP). Why the panel itself never runs as root, or offers a root shell over the web, is explained in why a hosting panel should never run as root.

The takeaway

The right order is keys first, then passwords off, then Fail2ban, then rate limits or an IP allowlist, and only then a port change if you want quieter logs. Do every step with an SSH session still open. After that, keeping OpenSSH patched matters just as much.

ZoPanel sets up UFW, Fail2ban and the SSH checks at install time, on the Free plan too. Install it with curl -fsSL https://get.zopanel.net | sudo bash on Ubuntu 22.04/24.04 or Debian 12/13, and see pricing when you need more sites.

Frequently asked questions

Do I still need Fail2ban if SSH passwords are disabled?

It's still worth having. With keys only, password brute force can't succeed, but bots keep opening thousands of connections, wasting resources and cluttering logs. Fail2ban bans them early, and it protects other services such as mail or FTP if you enable jails for them.

Does changing the SSH port stop brute force attacks?

It reduces automated scanning and makes logs quieter, but it won't stop anyone who scans every port. Use it as an extra layer after SSH keys and Fail2ban, not as a replacement.

What if Fail2ban bans my own IP?

Connect from another IP or through your VPS provider's web console, then run sudo fail2ban-client set sshd unbanip YOUR_IP. In ZoPanel you can also unban it on the Security page if you can still reach the panel from another address.

Which SSH key type should I use?

Ed25519 is a good choice for new keys: short, fast and secure. With a hardware security key, ed25519-sk requires a touch on every login. RSA is still fine for older systems that need it, at 3072 bits or more.

Why is Ubuntu 24.04 still listening on port 22 after I changed it?

Ubuntu 24.04 uses socket activation for SSH, so the port is set by ssh.socket. After changing Port, run sudo systemctl daemon-reload and then sudo systemctl restart ssh.socket, and confirm with ss -tlnp.