Port 22 on a public IP starts getting brute-forced within minutes of going live. You do not need anything exotic to stop it: key-only authentication plus a rate limiter removes essentially the entire risk. Here is the practical, in-order setup.
1. Use keys, then turn passwords off
Generate a modern key on your local machine and copy it up:
ssh-keygen -t ed25519 -C "you@laptop"
ssh-copy-id user@your-serverConfirm key login works, then disable passwords entirely so brute force has nothing to aim at:
PasswordAuthentication no
PermitRootLogin no
KbdInteractiveAuthentication no
# apply
sudo sshd -t && sudo systemctl restart sshPermitRootLogin: no, prohibit-password or yes?
PermitRootLogin takes four values. The OpenSSH default is prohibit-password: root may log in, but only with a key, never with a password.without-password is the old, deprecated name for the same thing.
| Value | Root can log in with | Use it when |
|---|---|---|
| no | nothing | You log in as a normal user and use sudo. The safest choice |
| prohibit-password | a key only (password and keyboard-interactive are off for root) | Automation needs root and runs with a key; this is the default |
| forced-commands-only | a key only, and only if that key has a command= option in authorized_keys | A backup or deploy key that may run exactly one command as root |
| yes | any method, including a password | Practically never on a public IP |
A value in sshd_config is not always the one sshd uses. For each keyword the first value it reads wins, and Debian and Ubuntu read the drop-in files in /etc/ssh/sshd_config.d/ first, so a cloud-init file there can quietly override your edit. Ask sshd for the setting it actually applies:
sudo sshd -T | grep -i permitrootlogin
grep -ri permitrootlogin /etc/ssh/sshd_config /etc/ssh/sshd_config.d/2. Add fail2ban
fail2ban watches the auth log and temporarily bans IPs that fail repeatedly. It neutralises the noise from bots even with passwords already off:
sudo apt install -y fail2ban python3-systemdCreate a minimal jail for SSH. backend = systemd makes it read sshd’s log from the journal: Debian 12 writes no /var/log/auth.log, and without that line fail2ban stops there with Have not found any log file for sshd jail. That is also why the service apt started fails on Debian 12 until this file is in place.
[sshd]
enabled = true
backend = systemd
maxretry = 4
bantime = 1h
findtime = 10msudo systemctl enable fail2ban
sudo systemctl restart fail2ban
sudo fail2ban-client status sshdYour own address in ignoreip, unbanning an IP, repeat offenders and jails for nginx are covered in the fail2ban guide for Ubuntu and Debian.
3. Should you change the port?
Moving SSH off 22 does not add real security (a scanner finds the new port in seconds), but it does cut log noise dramatically. It is cosmetic, not protective. If you do it, remember to open the new port in the firewall first. How to check which port sshd really listens on, and how ssh, scp and rsync connect to another one, is in SSH port 22: check it, connect on another.
4. Limit who can log in
Restrict SSH to the accounts that actually need it:
# in sshd_config
AllowUsers deploy admin5. Firewall the rest
Everything except the ports you serve should be closed. See the UFW guide for the full setup; the SSH essentials are:
sudo ufw allow OpenSSH
sudo ufw enableThe short version
- Keys only, passwords and root login off.
- fail2ban on the SSH jail.
- Firewall closed except for what you serve.
- An allow-list of login accounts.
That combination stops the overwhelming majority of attacks against a new VMHeaven Linux VPS. Every VMHeaven VPS has a browser VNC console in the panel, which works without SSH, so you can apply it confidently without fear of being locked out.
Frequently asked
Does changing the SSH port improve security?
Not meaningfully: a scanner finds the new port in seconds. It cuts log noise, which is nice, but it is cosmetic, not protective. Key-only auth is what matters.
Is fail2ban still useful if passwords are disabled?
Yes. It bans noisy IPs and reduces log clutter and load from constant scanning, even when key-only auth already blocks them.
What is the minimum effective setup?
Keys only with passwords and root login off, fail2ban on the SSH jail, a firewall closed except for what you serve, and an AllowUsers list.