Coding tips

How to Secure an SSH Server: 12 Steps to Harden sshd on Linux

To secure an SSH server, switch to SSH key authentication, disable password and root logins in /etc/ssh/sshd_config, limit which users can log in, firewall port 22 to trusted addresses where possible, add fail2ban, and keep OpenSSH updated.

By 10 min read 6 views
Cover image for How to Secure an SSH Server: 12 Steps to Harden sshd on Linux

To secure an SSH server, switch to SSH key authentication, disable password and root logins in /etc/ssh/sshd_config, limit which users can log in, firewall port 22 to trusted addresses where possible, add fail2ban, and keep OpenSSH updated. Always test the config with sudo sshd -t and keep your current session open until a new login works.

Any server with SSH open to the internet gets a steady stream of automated login attempts, often within minutes of going live. Most are bots trying common usernames and passwords. A few configuration changes make those attempts pointless.

This guide is for Ubuntu and Debian, but the sshd_config settings apply to almost any Linux or BSD system running OpenSSH. Every step is defensive and applies to servers you manage.

In this guide:

  • Before you start: don't lock yourself out
  • Step 1: update OpenSSH and the system
  • Step 2: create a non-root user with sudo
  • Step 3: set up SSH key authentication
  • Step 4: understand where sshd reads its settings
  • Steps 5 to 9: the hardening settings
  • Step 10: test and apply the changes
  • Step 11: firewall SSH with UFW
  • Step 12: add fail2ban
  • Should you change the SSH port?
  • Ongoing checks
  • Optional: add two-factor authentication to SSH
  • Quick hardening checklist
  • Frequently asked questions

Before you start: don't lock yourself out

Changing SSH settings on a remote server is one of the few ways to lock yourself out completely. Follow these rules every time:

  1. Keep your current SSH session open while you make changes. Existing sessions usually survive a restart of the SSH service.
  2. Test the config before restarting: sudo sshd -t prints nothing if the file is valid.
  3. Open a second terminal and log in again to confirm the new settings work, before you close the first.
  4. Know your provider's console. Most cloud hosts (AWS, DigitalOcean, Hetzner, Linode and others) offer a web or serial console that works even when SSH doesn't.
  5. Take a snapshot or back up /etc/ssh/sshd_config first: sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.

Step 1: update OpenSSH and the system

sudo apt update && sudo apt upgrade
ssh -V

Security fixes for OpenSSH arrive through normal system updates. On Ubuntu and Debian you can install the unattended-upgrades package so security patches are applied automatically:

sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades

Step 2: create a non-root user with sudo

Don't log in as root day to day. Create a normal user and give it sudo rights:

sudo adduser alice
sudo usermod -aG sudo alice

Log in as that user in a new terminal and check sudo whoami prints root before you go further.

Step 3: set up SSH key authentication

SSH keys replace passwords with a key pair: a private key that stays on your computer and a public key you put on the server. They're far harder to guess or steal than a password. On your own computer (not the server):

# Create an Ed25519 key, protected with a passphrase
ssh-keygen -t ed25519 -C "alice@laptop"

# Copy the public key to the server
ssh-copy-id alice@server.example.com

# Test: this should log in without asking for the server password
ssh alice@server.example.com
FileWhereShare it?
~/.ssh/id_ed25519Your computerNever: this is the private key
~/.ssh/id_ed25519.pubYour computerYes: this goes on servers
~/.ssh/authorized_keysThe server, in the user's homeLists public keys allowed to log in

On Windows 10 and 11, the same ssh-keygen command works in PowerShell. If ssh-copy-id isn't available, paste the contents of the .pub file into ~/.ssh/authorized_keys on the server and run chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys.

Use a passphrase on the key. If you need a strong one, our password generator can create a memorable passphrase.

Step 4: understand where sshd reads its settings

OpenSSH's server reads /etc/ssh/sshd_config. On current Ubuntu and Debian releases, that file starts with an Include /etc/ssh/sshd_config.d/*.conf line. For most settings, the first value sshd finds wins, so a file in sshd_config.d can override what you set further down the main file.

The cleanest approach is to put your hardening in its own drop-in file with a low number so it's read first:

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

Also check for existing drop-ins, such as one added by cloud-init that sets PasswordAuthentication yes:

ls /etc/ssh/sshd_config.d/
sudo sshd -T | grep -Ei 'passwordauth|permitroot|kbdinteractive|maxauth'

sshd -T prints the settings sshd will actually use, which is the best way to confirm your changes have taken effect.

Steps 5 to 9: the hardening settings

Put these lines in your drop-in file, replacing alice with your username(s):

# /etc/ssh/sshd_config.d/00-hardening.conf
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
PermitEmptyPasswords no
AllowUsers alice
MaxAuthTries 3
LoginGraceTime 30
X11Forwarding no
ClientAliveInterval 300
ClientAliveCountMax 2
SettingOpenSSH defaultHardened valueWhy
PermitRootLoginprohibit-passwordnoAttackers always try "root"; log in as a normal user and use sudo
PasswordAuthenticationyesnoStops password-guessing attacks entirely
KbdInteractiveAuthenticationyesnoCloses another route for password prompts
AllowUsers(all users)named users onlyService and test accounts can't log in over SSH
MaxAuthTries63Fewer attempts per connection
LoginGraceTime120 seconds30 secondsDrops half-open login attempts sooner
X11Forwardingno (often yes in distro configs)noTurn off features you don't use
ClientAliveInterval / CountMax0 / 3300 / 2Closes dead sessions after about 10 minutes

The defaults above come from the official sshd_config manual; your distribution's shipped config may differ. Recent OpenSSH versions also include PerSourcePenalties, which slows down addresses that repeatedly fail to authenticate.

Step 10: test and apply the changes

# 1. Check syntax: no output means OK
sudo sshd -t

# 2. Confirm the effective settings
sudo sshd -T | grep -Ei 'passwordauth|permitroot|allowusers'

# 3. Reload SSH (Ubuntu/Debian service name is "ssh")
sudo systemctl reload ssh

# 4. In a NEW terminal, confirm you can still log in
ssh alice@server.example.com

# 5. Confirm password logins are refused
ssh -o PubkeyAuthentication=no alice@server.example.com
# Expect: Permission denied (publickey).

On some systems the service is called sshd rather than ssh. Recent Ubuntu releases also use socket activation for SSH, which matters if you change the port (see below).

Step 11: firewall SSH with UFW

A firewall limits who can even reach the SSH port. On Ubuntu, UFW makes this simple. Allow SSH before enabling the firewall, or you'll cut yourself off:

sudo ufw allow OpenSSH        # or: sudo ufw limit OpenSSH
sudo ufw enable
sudo ufw status verbose
RuleEffectBest for
ufw allow OpenSSHAllows port 22 from anywhereWhen you connect from changing locations
ufw limit OpenSSHAllows port 22 but blocks addresses making repeated connections in a short timeA simple brute-force brake
ufw allow from 203.0.113.10 to any port 22Only one IP can connectFixed office or home IPs
VPN or Tailscale onlySSH isn't exposed to the internet at allThe strongest option for small teams

Cloud servers often have a provider firewall or security group as well. Restricting port 22 there gives you a second layer.

Step 12: add fail2ban

fail2ban watches logs and temporarily bans IP addresses that keep failing to log in. With passwords disabled it's less critical, but it cuts down log noise and wasted resources.

sudo apt install fail2ban
sudo nano /etc/fail2ban/jail.local
[sshd]
enabled  = true
maxretry = 5
findtime = 10m
bantime  = 1h
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd

Put your own changes in jail.local rather than editing jail.conf, so updates don't overwrite them. If you lock out your own IP, unban it with sudo fail2ban-client set sshd unbanip YOUR.IP.

Should you change the SSH port?

Moving SSH from port 22 to, say, 2222 doesn't make it more secure against a determined attacker, because a port scan finds it quickly. What it does do is cut automated noise in your logs a great deal. If you change it:

  1. Allow the new port in the firewall first: sudo ufw allow 2222/tcp.
  2. Set Port 2222 in sshd's config. On Ubuntu releases that use ssh.socket, the listening port is controlled by the socket unit, so check Ubuntu's documentation for your release and run sudo systemctl daemon-reload and sudo systemctl restart ssh.socket.
  3. Test with ssh -p 2222 alice@server before removing the old firewall rule.

Treat a changed port as tidiness, not protection. Key-only logins do the real work. You can see which ports a server exposes with our Nmap basics tutorial.

Ongoing checks

TaskCommandHow often
See recent successful loginslast -a | headWeekly
See failed attemptssudo journalctl -u ssh --since today | grep -i failWhen investigating
Review authorised keyscat ~/.ssh/authorized_keysWhen people leave or laptops change
Check effective configsudo sshd -TAfter updates
List supported algorithmsssh -Q kex, ssh -Q cipherWhen auditing
Check fail2ban banssudo fail2ban-client status sshdMonthly

Remove keys for anyone who no longer needs access. A forgotten key in authorized_keys is a forgotten way in.

Optional: add two-factor authentication to SSH

For extra protection on important servers, you can require a second factor as well as a key. Two common approaches:

MethodHow it worksTrade-off
FIDO2 security key (ssh-keygen -t ed25519-sk)The private key needs a tap on a hardware key such as a YubiKeyVery strong; needs OpenSSH 8.2 or later on both sides and a key per person
TOTP via PAM (e.g. libpam-google-authenticator)Asks for a 6-digit code after the keyWorks with any phone; more configuration, and needs KbdInteractiveAuthentication turned back on for that step
Bastion or jump hostUsers connect through one hardened server with ssh -JOne place to monitor and patch

For most small servers, a passphrase-protected Ed25519 key plus the firewall and fail2ban steps above is already a strong setup. Add hardware keys when several people have admin access or the server holds sensitive data.

Quick hardening checklist

CheckDone when
System updatedapt upgrade shows nothing pending; unattended-upgrades enabled
Non-root sudo userYou can log in as that user and run sudo
Key login worksSSH logs in without asking for the server password
Passwords offsshd -T shows passwordauthentication no
Root login offsshd -T shows permitrootlogin no
Users limitedAllowUsers lists only the people who need access
Firewall onufw status shows SSH allowed and the firewall active
fail2ban runningfail2ban-client status sshd shows the jail
Recovery route knownYou've opened your provider's console at least once

Work down the table on every new server you build, and save your drop-in config file so the next one takes minutes.

Frequently asked questions

What is the most important step to secure an SSH server?

Switch to SSH key authentication and set PasswordAuthentication no. That stops password-guessing attacks, which make up most automated SSH attacks.

How do I disable root login over SSH?

Add PermitRootLogin no to /etc/ssh/sshd_config or a drop-in file in /etc/ssh/sshd_config.d, test with sudo sshd -t, then reload the service with sudo systemctl reload ssh.

Why didn't my sshd_config change take effect?

On Ubuntu and Debian, files in /etc/ssh/sshd_config.d are included at the top of the main file, and the first value sshd reads usually wins. Run sudo sshd -T to see the settings actually in use.

Does changing the SSH port improve security?

Only a little. It reduces automated log noise, but a port scan finds the new port quickly. Key-only logins, firewall rules and updates provide the real protection.

Is fail2ban still needed if passwords are disabled?

It is less critical, but it still reduces log noise and blocks persistent scanners. It is a useful extra layer rather than a replacement for key authentication.

Which SSH key type should I use?

Ed25519 is a good default for modern systems: ssh-keygen -t ed25519. Protect the private key with a passphrase.

How do I avoid locking myself out when hardening SSH?

Keep your current session open, test with sudo sshd -t, open a second terminal to confirm you can log in, allow SSH in the firewall before enabling it, and know how to reach your provider's console.

How do I check who has logged in to my server over SSH?

Use last -a to see recent logins and sudo journalctl -u ssh to review SSH logs, including failed attempts.

Last checked: 20 October 2026. Figures, settings and guidance can change, so confirm anything important with the sources linked above.

Share X LinkedIn WhatsApp Facebook
Was this helpful?