# 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.

Source: https://zopanel.net/blog/ssh-brute-force-protection  
Updated: 2026-10-21

## 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:

```bash
# 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):

```bash
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:

```bash
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:

```bash
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:

```bash
sudo nano /etc/ssh/sshd_config.d/00-hardening.conf
```

```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:

```bash
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.

```bash
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`:

```ini
[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.

```bash
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](https://www.openssh.com/txt/release-9.8)). 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.

```bash
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:

```bash
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.

```bash
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](/blog/secure-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](/docs/security):

- **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](/docs/ftp-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](/blog/secure-hosting-panel-architecture).

## 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](/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`.
