A step-by-step guide to locking down SSH on a new Linux server: key-only auth, sane sshd settings, and the tradeoffs that actually matter.
The first thing exposed on a new VPS is SSH, and it is the part most worth getting right before you put anything else on the box. This is a practical walkthrough for hardening the daemon on a fresh Linux install. Every change here comes with a reason, and where there is a real tradeoff, it is called out rather than glossed over.
Work through this in one session, keep a second session open while you test, and do not close your working connection until you have confirmed the new configuration lets you back in.
Start with key authentication, not a stronger password
Password authentication is the single largest source of noise and risk on a public-facing SSH port. The fix is not a longer password. It is removing passwords from the equation entirely.
Generate a key on your local machine. Prefer Ed25519. It produces short keys, verifies quickly, and avoids the parameter choices that make RSA easy to get wrong.
ssh-keygen -t ed25519 -C "workstation-2024"
Use RSA only when you must talk to something old that cannot parse Ed25519, and if you do, use 4096 bits:
ssh-keygen -t rsa -b 4096 -C "legacy-client"
Copy the public key to the server for the user you intend to log in as:
ssh-copy-id user@your-server
If ssh-copy-id is not available, append the contents of your .pub file to ~/.ssh/authorized_keys on the server, then make sure permissions are tight: 700 on ~/.ssh and 600 on authorized_keys. SSH silently ignores keys in a directory that is group or world writable, and this is the most common reason a correct key still gets rejected.
Open a new terminal and confirm you can log in with the key before changing anything else. If that fails, fix it now while password login is still available.
Edit sshd_config deliberately
The daemon configuration is where most of the hardening happens. Make these changes one at a time and understand each.
Disable password authentication
PasswordAuthentication no
KbdInteractiveAuthentication no
Both lines matter. Disabling only the first while leaving keyboard-interactive enabled can leave a fallback path open on some distributions. Turn off both unless you are deliberately running an interactive multi-factor flow.
Restrict root login
PermitRootLogin prohibit-password
The choice here is real. no blocks root over SSH entirely, which is the cleaner posture if you always operate through a sudo-enabled user. prohibit-password allows root only with a key, which is useful for automation that genuinely needs root. Pick based on how you actually administer the box. Do not leave it at the permissive default.
Limit who can connect
AllowUsers deploy admin
An explicit allow list means a newly created or compromised service account cannot be used to log in over SSH unless you put it here on purpose. This is cheap and effective, and it scales fine for a handful of operators.
Reduce the authentication window
MaxAuthTries 3
LoginGraceTime 20
These bound how long an unauthenticated connection can sit and how many attempts it gets. With password auth already off, the practical effect is to drop slow probes quickly and free up connection slots.
The port change question
Moving SSH off port 22 is the most argued-about change on this list. Be honest about what it does. It does not improve cryptographic security. A targeted attacker scans all ports. What it does do is remove the constant background noise of automated bots hammering 22, which keeps your logs readable and your MaxAuthTries counters quiet.
If you change it, document it, and account for it in firewall rules. Anything above 1024 is fine. The cost is one more thing to remember and one more flag on every connection. For a single-operator setup it is a reasonable trade. For a team it can become a support burden. Decide accordingly.
Port 2222
Validate, then reload
Never restart the daemon blind. Test the configuration first:
sshd -t
If it returns nothing, the syntax is valid. Reload rather than restart so existing sessions are not dropped:
systemctl reload ssh
With your original session still open, start a fresh connection in a new terminal and confirm key login works under the new rules. Only once that succeeds should you close the session you started with. This habit has saved more people from locking themselves out than any single config line.
Firewall the port to the connections you expect
SSH hardening and host firewalling are separate jobs that work together. Allow your SSH port and deny the rest by default. With ufw:
ufw default deny incoming
ufw default allow outgoing
ufw allow 2222/tcp
ufw enable
If you administer from a fixed network, you can scope the rule to a source range so the port is not exposed to the entire internet. That is the strongest single improvement available, but only commit to it if your source address is genuinely stable. Locking SSH to an IP you lose access to is its own kind of outage.
Rate-limit repeat offenders
With password auth disabled, brute force is already a dead end, but a log-watching tool still earns its place by banning hosts that probe persistently and trimming noise. fail2ban covers this well with its default SSH jail:
apt install fail2ban
systemctl enable --now fail2ban
The shipped sshd jail watches the auth log and bans repeat sources for a set window. Tune maxretry and bantime to taste, but the defaults are sane and you do not need to over-engineer this.
A few settings worth knowing about
- ClientAliveInterval and ClientAliveCountMax close idle or dead sessions so half-open connections do not linger. Set the interval to something like 300 seconds with a count of 2.
- X11Forwarding no unless you specifically need graphical forwarding. On a server you almost never do.
- AllowAgentForwarding and AllowTcpForwarding can be disabled if the box is not used as a jump host. If it is, leave them on deliberately, not by default.
What this buys you
After these changes, your server accepts SSH only from holders of a private key, only for named users, and only on a port and from sources you chose. Password guessing is impossible because passwords are not accepted. Probes get banned. Idle sessions close themselves. None of this is exotic, and that is the point: the strongest baseline is built from a small number of well-understood settings, applied in the right order, and tested before you trust them.
Do this once on a new RackWorks VPS, capture the config so you can reproduce it, and you have a hardened front door before the first real workload lands on the box.