SSH hardening should be staged, not rushed. The safe pattern is to keep your current session open, add a second verified login path, test configuration syntax, and only then disable weaker access.
This guide is written as a cautious production checklist. It does not require exploit testing, but it does require discipline: do not close your existing SSH session while changing authentication, firewall, or service settings.
Verification and safety note
This page is a source-checked SSH hardening checklist, not a claim that the commands were run against your production server. SSH and firewall changes can lock you out, so the guide favors staged changes, syntax checks, official documentation, and rollback paths over one-shot hardening scripts.
Before applying it, confirm your Ubuntu/OpenSSH version, provider console access, and a second working login path. Treat every command that changes SSH, users, firewall, or service state as a maintenance-window action.
Before you change anything
Open a second terminal and confirm you can log in independently. If you are connected to a VPS, also confirm you have provider console or rescue access before touching SSH settings.
Minimum safety checks:
- one current SSH session stays open;
- one separate SSH login succeeds;
- you know the server’s public IP or private Tailscale/WireGuard address;
- you have console/rescue access from the VPS provider;
- you know how to revert the file you are editing.
If any of those are missing, fix that first.
Step 1: Create or confirm a non-root sudo user
Use a non-root admin user for daily access. Root login should not be the normal path.
Run these on Ubuntu 24.04 as root or with sudo, replacing adminuser with your real admin username:
adduser adminuser
usermod -aG sudo adminuser
If the user already exists, confirm group membership instead:
id adminuser
The user should belong to the sudo group before you rely on that account for administration.
Step 2: Add SSH key authentication before disabling passwords
Do not disable password login until key login works from a fresh terminal.
From your local machine, copy your public key:
ssh-copy-id adminuser@SERVER_ADDRESS
Or manually place the public key in:
/home/adminuser/.ssh/authorized_keys
Then test a fresh login:
ssh adminuser@SERVER_ADDRESS
If this fails, stop. Do not edit password settings yet.
Step 3: Put SSH hardening in a drop-in file
On Ubuntu, OpenSSH server configuration can be changed in the main file or through included drop-in files. Ubuntu’s OpenSSH documentation notes that /etc/ssh/sshd_config includes /etc/ssh/sshd_config.d/*.conf, so a local drop-in is often cleaner than editing the whole file.
Create a local hardening file:
sudo nano /etc/ssh/sshd_config.d/99-local-hardening.conf
Start with conservative settings:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
KbdInteractiveAuthentication no
X11Forwarding no
These settings assume key login already works. If key login has not been verified, do not apply them.
Step 4: Test SSH configuration before restarting
Always test syntax before reloading or restarting SSH.
sudo sshd -t
No output usually means the syntax check passed. If there is an error, fix it before restarting anything.
Then reload SSH:
sudo systemctl reload ssh
On Ubuntu, the service is commonly named ssh. On Ubuntu 24.04 systems using socket activation, you may also see ssh.socket involved. Confirm what your server is using before changing automation around it:
systemctl status ssh --no-pager
systemctl status ssh.socket --no-pager
Step 5: Test a new login before closing the old one
Keep your original SSH session open. In a new terminal, try logging in again:
ssh adminuser@SERVER_ADDRESS
Then confirm sudo works:
sudo -v
Only close the old session after the new session works.
Step 6: Limit where SSH is reachable
SSH key login is good. Private reachability is better.
For small servers, consider one of these patterns:
- SSH only over Tailscale;
- SSH only over WireGuard;
- SSH allowed only from trusted source IPs;
- SSH behind a temporary access path during maintenance.
If using UFW, be careful. Allow your safe SSH path before enabling or tightening the firewall:
sudo ufw allow OpenSSH
sudo ufw status verbose
If SSH should only be reached over a private interface, test that path first and document exactly what is allowed.
Step 7: Keep a rollback path
Before hardening, save the current effective configuration and the file you changed.
sudo cp /etc/ssh/sshd_config /root/sshd_config.backup.$(date +%F)
If you used a drop-in file and need to revert from console access, move it away and reload SSH:
sudo mv /etc/ssh/sshd_config.d/99-local-hardening.conf /root/99-local-hardening.conf.disabled
sudo systemctl reload ssh
Do not rely on memory during a lockout. Write the rollback path down before changing access.
A safer minimum baseline
A reasonable first SSH baseline is:
- non-root sudo user;
- key-based login verified from a fresh terminal;
- root SSH login disabled;
- password SSH login disabled only after key login works;
- SSH config syntax tested before reload;
- firewall rules reviewed before activation;
- provider console/rescue access confirmed;
- private access path planned for admin work.
That baseline is not exotic. It is just careful.
Common lockout causes
Disabling passwords before testing keys
This is the classic mistake. A public key in the wrong user’s home directory will not save you after password login is disabled.
Restarting SSH after a syntax error
Run sudo sshd -t before reloads. Syntax checks are cheap; lockouts are expensive.
Enabling UFW before allowing the active SSH path
A firewall can lock you out even if SSH itself is configured correctly.
Closing the only working session
Keep the old session open until a new one works.
Internal links
- Secure Remote Access for a Linux Server
- Linux Server Security
- How to Decide What Should Be Public on a Linux Server
- How to Recover When You Break SSH Access to a VPS
- How to Install Tailscale on Ubuntu for Private SSH
Sources
- Ubuntu Server documentation: OpenSSH server — https://ubuntu.com/server/docs/how-to/security/openssh-server
- Ubuntu manpage:
sshd_config(5)— https://manpages.ubuntu.com/manpages/noble/man5/sshd_config.5.html - OpenSSH manual:
sshd(8)— https://man.openbsd.org/sshd - UFW manual page — https://manpages.ubuntu.com/manpages/noble/man8/ufw.8.html
Before you apply this on a production server
Keep your current SSH session open, test each change in a second terminal, and confirm your VPS provider console or rescue mode works before touching firewall or SSH settings.
Use read-only checks such as sshd -t, systemctl status ssh --no-pager, systemctl status ssh.socket --no-pager, and ufw status verbose before and after changes. Do not close the working session until key-based login, SSH reload, and firewall status have all been checked.
Bottom line
SSH hardening is not about one clever setting. It is about changing access without losing access.
Keep a working session open, verify key login, test syntax, reload carefully, and only then remove weaker paths.