SSH Hardening Checklist: How to Secure a Linux VPS
SSH hardening for a new Ubuntu 24.04 VPS, step by step: SSH keys, sshd settings, UFW and Fail2ban, with a test at each step so you stay connected.
This SSH hardening checklist takes a new Ubuntu 24.04 LTS VPS from password logins as root to key-only logins for one named user, behind a firewall and Fail2ban. Every risky step includes a test you run while your current session stays open, and notes where Debian 12 differs. If your server runs Windows, our tips for securing Remote Desktop cover the same ground for RDP.
SSH hardening checklist at a glance
SSH hardening on a new server takes eleven steps, and the order matters: get a key login working for a sudo user before you switch off root and password logins, and allow SSH before you turn on the firewall.
| Step | Lockout risk |
|---|---|
| 1. Automatic security updates | None |
| 2. A sudo user | None |
| 3-4. SSH key login | None |
| 5-7. No root login, no passwords | Yes: test a second login |
| 8. A new SSH port (optional) | Yes |
| 9. UFW firewall | Yes |
| 10. Fail2ban | It can ban your own address |
| 11. Unused services off | None |
Prepare the server: updates and a sudo user
As root, sudo is optional in the first two steps. Throughout, replace deploy with your user name and 203.0.113.10 with your server's IP address.
1. Update the system and turn on automatic security updates
Install pending updates first: apt update refreshes the package lists and apt upgrade installs the upgrades. [1]
sudo apt update
sudo apt upgradeOn Ubuntu, security updates install automatically through the unattended-upgrades package, which is installed by default and runs once a day. Both values in /etc/apt/apt.conf.d/20auto-upgrades should be "1", and a dry run shows what it would install. [1]
cat /etc/apt/apt.conf.d/20auto-upgrades
sudo unattended-upgrade --dry-run -vAPT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";It does not reboot on its own, because Unattended-Upgrade::Automatic-Reboot is false by default, so reboot by hand when an update needs it. [1] On Debian 12, if the package or the file is missing, install unattended-upgrades and create the file with the two lines above.
2. Create a non-root sudo user
Do admin work as a named user with sudo, which asks for that user's own password, not root's. Members of the sudo group get full root access through it. [2]
sudo adduser deploy
sudo adduser deploy sudoadduser asks for a password and a few optional details. [2] Make the password long: SSH will use keys, but sudo still asks for it. Then check the account; sudo whoami should print root.
su - deploy
sudo whoamiOn Debian 12, if the sudo command is missing, install it as root with apt install sudo first.
Set up SSH key login
3. Generate an ed25519 key on your computer
Create the key pair on your own computer, not on the server. Ubuntu recommends the ed25519 type for its shorter keys and lower computing cost, and ssh-keygen asks for a passphrase that protects the private key. [3]
ssh-keygen -t ed25519Keep the default file name. The private key is ~/.ssh/id_ed25519; the public key you copy to servers is ~/.ssh/id_ed25519.pub. [3]
On Windows, run the same command in PowerShell; it saves the pair in the .ssh folder of your user profile. Microsoft advises against an empty passphrase and warns that anyone who gets the private key can sign in as you. [4]
ssh-keygen -t ed255194. Copy the public key to the server
On Linux and macOS, ssh-copy-id appends your public key to ~/.ssh/authorized_keys for the user on the server. [3]
ssh-copy-id deploy@203.0.113.10Microsoft's guide copies keys with PowerShell or scp instead. [4] For a Linux server, this line appends your key and makes the folder and the file writable by you only:
Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub | ssh deploy@203.0.113.10 "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"sshd checks these permissions before it accepts a login, through StrictModes, which is on by default. [5] Then run ssh deploy@203.0.113.10 in a new terminal: you should get in without the account password. If not, follow the server's SSH log while you retry: [3]
sudo journalctl -fu ssh.serviceLock down sshd: root login, passwords and users
The next steps switch off root and password logins. Start only after your key login as the new user works, and keep that session open until a second login succeeds.
5. Write your settings in a drop-in file
On Ubuntu, /etc/ssh/sshd_config starts with Include /etc/ssh/sshd_config.d/*.conf, and OpenSSH keeps the first value it reads for most settings, so files in that folder override the main file. [3] Included files are read in lexical order and, for each keyword, the first value sshd obtains wins, [5] so name yours 00-hardening.conf.
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
AllowUsers deployFrom the sshd_config manual:
- PermitRootLogin no stops root from logging in over SSH. The default, prohibit-password, refuses passwords for root but still accepts keys. [5]
- PasswordAuthentication no turns off password logins, which are allowed by default. [5]
- KbdInteractiveAuthentication no turns off keyboard-interactive logins, a separate method that is also on by default; its old name is ChallengeResponseAuthentication. [5]
- AllowUsers deploy (optional) lets only the listed users log in. Unlike most keywords, each AllowUsers line adds to the list. [5] Spell the name exactly, or nobody gets in.
Treat the sudo account like root from now on: Ubuntu's openssh notes call any account you can escalate to root from equivalent to root against outside attacks. [6] On Debian 12, first confirm the Include line with grep -n Include /etc/ssh/sshd_config.
6. Check for files that turn passwords back on
Since the first value wins, any file that sorts before yours beats it. Search the folder:
ls /etc/ssh/sshd_config.d/
sudo grep -riE 'passwordauthentication|kbdinteractive|permitrootlogin|allowusers' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/On cloud images, cloud-init writes the SSH settings it manages to /etc/ssh/sshd_config.d/50-cloud-init.conf whenever sshd_config includes that folder. [7] If that file says PasswordAuthentication yes, it beats every file that sorts after it, which is why yours starts with 00. Change it to no too, so the files agree.
7. Test the config and restart SSH
Test the files before you restart: a bad setting can stop sshd from starting, and SSH may be your only way in. [3]
sudo sshd -t
sudo systemctl restart ssh.serviceFix anything sshd -t reports first. After the restart, keep your session open and test from a second terminal:
ssh deploy@203.0.113.10
ssh root@203.0.113.10The first login should work and the second should be refused. If your login fails, undo the change in the open session and check the SSH log from step 4. [3]
Should you change the SSH port?
Only if you want quieter logs. A new port moves SSH off the default, 22, [5] so bots that only try port 22 miss it, but a scan of all ports still finds SSH. Keys and disabled passwords do the real work.
8. Change the port on Ubuntu 24.04 (optional)
If UFW or another firewall is already on, allow the new port first. Keep your session open until a login on the new port works.
On Ubuntu 24.04, ssh.socket listens on port 22 through systemd socket activation. A generator turns the Port lines in sshd_config and its drop-in files into socket settings, so after a port change you reload systemd and restart ssh.socket. [6]
sshd accepts more than one Port line, [5] so keep 22 while you test the new port:
Port 22
Port 2222sudo sshd -t
sudo systemctl daemon-reload
sudo systemctl restart ssh.socketTest the new port from a second terminal. Then delete Port 22, repeat the three commands and use port 2222 in steps 9 and 10.
ssh -p 2222 deploy@203.0.113.10On Debian 12, check first whether the socket is in use. If it is inactive, restarting the service applies the change.
systemctl is-active ssh.socket
sudo systemctl restart ssh.serviceFirewall, Fail2ban and open ports
9. Allow SSH in UFW, then enable it
Enabling UFW can drop your SSH session, and from then on it blocks every port you have not allowed. Add the SSH rule before you run ufw enable.
UFW is installed disabled and, once enabled, denies incoming traffic by default. Its manual warns that ufw enable may drop SSH connections, so add the SSH rule first. [8]
Use limit for SSH rather than allow. A limit rule still allows connections but denies an address that tries to open 6 or more within 30 seconds. Do not add both rules, because UFW uses the first rule that matches. [8]
sudo ufw limit 22/tcp
sudo ufw enable
sudo ufw status verboseUFW asks for confirmation when you enable it over SSH. [8] If you moved SSH, limit 2222/tcp instead. On Debian 12, install UFW first if the command is missing. To take SSH off the public internet altogether, reach it only through a WireGuard private network. For protection further out, see our DDoS protection checklist for VPS hosting.
10. Add Fail2ban for the sshd jail
Fail2ban can ban your own IP address after repeated failed logins. Put your fixed IP address in ignoreip if you have one, and keep your session open while you test.
On Ubuntu 24.04, the package ships /etc/fail2ban/jail.d/defaults-debian.conf, which enables the sshd jail, sets backend = systemd and bans through nftables. [9] The systemd backend reads the systemd journal, so the jail needs no separate log file. [10]
sudo apt install fail2ban
cat /etc/fail2ban/jail.d/defaults-debian.confFail2ban's own jail.conf bans an address for 10 minutes once it fails 5 times within 10 minutes, and tells you not to edit that file: put your changes in jail.local. [10]
[sshd]
enabled = true
backend = systemd
maxretry = 5
findtime = 10m
bantime = 1h
ignoreip = 127.0.0.1/8 ::1 198.51.100.7Replace 198.51.100.7 with your own fixed address, or delete it. The stock jail uses port = ssh, so add port = 2222 if you moved SSH. [10] On Debian 12, keep backend = systemd here and install python3-systemd, the systemd Python library this backend uses. [10] Then restart Fail2ban and check that it runs:
sudo systemctl restart fail2ban
sudo systemctl status fail2ban11. Check what else is listening
Every open port is another way in. UFW's listening report shows each listening TCP port and open UDP port, its address (* means every interface), the program behind it and the rules that apply. [8]
sudo ufw show listeningA service bound to 127.0.0.1 is reachable only from the server. Switch off public ones you do not need with systemctl disable --now, as Ubuntu's openssh notes do for ssh.socket. [6]
sudo systemctl disable --now SERVICE_NAMEIf you lock yourself out
Most lockouts come from closing the last working session too early. If it happens anyway:
- Use a session that is still open to undo the last change, run
sshd -tand restart. - Pause if UFW refused you: its limit rule counts attempts over 30 seconds. [8]
- Wait out a Fail2ban ban, which lasts for
bantime, an hour in thejail.localabove, [10] or connect from another network, such as a phone hotspot. - Rebuild the server as a last resort. On StealthRDP, the Help Center explains how to rebuild a server from the client area: pick the operating system, wait 3-5 minutes and connect over SSH. A rebuild installs a fresh system, so you restore your data from a backup; our guide to automated VPS backups helps you set one up in time.
Unsure? StealthRDP support is available 24/7 through Telegram, client-area tickets and info@stealthrdp.com.
FAQ
What is SSH hardening?
SSH hardening means changing the SSH server's settings so fewer accounts and login methods get in: keys instead of passwords, no root login and, optionally, only named users. OpenSSH accepts password and keyboard-interactive logins by default, so those come first. [5]
Why does PasswordAuthentication no not work on Ubuntu?
Another file probably sets it first. Ubuntu's sshd_config includes /etc/ssh/sshd_config.d/ at the very top, and OpenSSH keeps the first value it finds. [3] On cloud images, check 50-cloud-init.conf there, where cloud-init writes its SSH settings. [7]
Does changing the SSH port improve security?
Only a little. It moves SSH off the default port 22, which bots that only try that port then miss, but a full port scan still finds it. [5] Keys and disabled passwords do far more.
Should I disable root login over SSH?
Yes, once a sudo user can log in with a key. PermitRootLogin no refuses root entirely, while the default, prohibit-password, still accepts root keys. [5] Ubuntu's openssh notes add that your sudo account deserves the same care as root. [6]
Do I need Fail2ban if SSH uses keys only?
It is optional but useful: it bans addresses that keep failing, by default after 5 failures within 10 minutes. [10] On Ubuntu 24.04, the package enables its sshd jail by default. [9]
Is UFW enabled by default on Ubuntu?
No. UFW is installed disabled, and once enabled it denies incoming connections that no rule allows. [8] Add a rule for SSH before you run ufw enable.
Sources & references
- Automatic updates — Ubuntu Server documentation (accessed 2026-11-03)
- User management — Ubuntu Server documentation (accessed 2026-11-03)
- OpenSSH server — Ubuntu Server documentation (accessed 2026-11-03)
- Key-based authentication in OpenSSH for Windows — Microsoft Learn (accessed 2026-11-03)
- sshd_config(5): OpenSSH daemon configuration file — OpenBSD manual pages (accessed 2026-11-03)
- openssh: debian/README.Debian (ubuntu/noble-updates) — Ubuntu (Launchpad) (accessed 2026-11-03)
- cloudinit/ssh_util.py — GitHub (canonical/cloud-init) (accessed 2026-11-03)
- ufw - program for managing a netfilter firewall — Ubuntu Manpages (accessed 2026-11-03)
- fail2ban: debian/debian-files/jail.d_defaults-debian.conf (ubuntu/noble-updates) — Ubuntu (Launchpad) (accessed 2026-11-03)
- fail2ban 1.0.2: config/jail.conf — GitHub (fail2ban/fail2ban) (accessed 2026-11-03)
Continue learning
VPS Control Panels and Management Tools: 6 Compared
Compare VPS control panels such as Plesk, cPanel, Webmin, Virtualmin and DirectAdmin on price, features and skill level, and learn how to choose one.
High Availability VPS: How to Design Automated Failover
Design a high availability VPS setup with redundancy, replication, automated failover and monitoring, and see how VPS and cloud compare for uptime.
Server Uptime Monitoring: 7 Tools to Compare
Compare server uptime monitoring tools, what to look for in alerts and checks, and when a self-hosted uptime monitor is enough.
