2026-03-31 · SSHLinuxSecurityDevOps

SSH Hardening on a Fresh Linux Server: The Practical Guide

Every new VPS comes with SSH wide open. Password authentication enabled. Root login allowed. Port 22 listening on all interfaces. Within minutes of the server coming online, bots are already trying passwords. Here's how to close those gaps in order — without accidentally locking yourself out.

SSH hardening is one of those tasks that feels straightforward until you lock yourself out of your own server. The order of operations matters a lot here. Do the steps wrong and you're looking at a rescue console or a full server rebuild.

This guide does everything in the right order. Read it once before running any commands.

Before you start: open a second SSH session

This is not optional. Before making any changes to SSH configuration, open a second terminal and connect to your server. Keep it open. If your primary session disconnects, you have a working backup. If your backup also disconnects — you've locked yourself out and need the provider's console.

# Terminal 1: your working session where you make changes
# Terminal 2: keep this connected as a backup the entire time

# Verify you can connect before starting:
ssh user@your-server-ip "echo connected"

Step 1: Set up SSH key authentication

Do this first, before disabling password authentication. If you disable passwords before setting up keys, you lock yourself out.

# On your LOCAL machine — generate a key if you don't have one:
ssh-keygen -t ed25519 -C "your-server-name"

# Copy your public key to the server:
ssh-copy-id user@your-server-ip

# Verify key auth works — open a NEW terminal and connect:
ssh user@your-server-ip
# If this connects without asking for a password, keys are working

Only proceed to the next step after confirming key authentication works in a fresh terminal.

Step 2: Disable password authentication

sudo nano /etc/ssh/sshd_config

# Find and change these lines:
PasswordAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys

Don't restart SSH yet. Make all your config changes first, then restart once.

Step 3: Disable root login

# In /etc/ssh/sshd_config:
PermitRootLogin no

If you need to run commands as root, use sudo from your regular user account. Direct root SSH login is unnecessary and increases your attack surface.

Step 4: Limit login attempts

# In /etc/ssh/sshd_config:
MaxAuthTries 3
MaxSessions 5
LoginGraceTime 30

MaxAuthTries 3 limits how many password attempts before the connection is dropped. LoginGraceTime 30 gives users 30 seconds to authenticate before the connection times out.

Step 5: Disable unused authentication methods

# In /etc/ssh/sshd_config:
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no
PrintMotd no

Unless you specifically need X11 forwarding or TCP tunneling, disable them. These features increase attack surface without benefit for most setups.

Step 6: Apply the config

# Test the config before restarting:
sudo sshd -t

# If no errors, restart SSH:
sudo systemctl restart sshd

# Verify SSH is running:
sudo systemctl status sshd

Critical: After restarting SSH, test that your key authentication still works from your backup terminal before closing your primary session. If the backup terminal can connect — you're done. If it can't connect — something went wrong and you still have your primary session to fix it.

Step 7: Set up fail2ban

sudo apt install fail2ban -y

sudo tee /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime  = 1h
findtime = 10m
maxretry = 3
backend  = systemd

[sshd]
enabled  = true
port     = ssh
maxretry = 3
bantime  = 24h
EOF

sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd

Optional: change the SSH port

Changing from port 22 to a high-numbered port (e.g., 2222 or 22022) eliminates the vast majority of automated scanning. It's security through obscurity — not a substitute for proper hardening — but it drastically reduces log noise.

# In /etc/ssh/sshd_config:
Port 2222

# Update UFW to allow the new port:
sudo ufw allow 2222/tcp
sudo ufw delete allow 22/tcp  # Only after confirming the new port works

# Restart SSH:
sudo systemctl restart sshd

# Connect on the new port to verify:
ssh -p 2222 user@your-server-ip

Important: update your firewall rules before restarting SSH on the new port. If you close port 22 in UFW before opening the new port, you lock yourself out.

Verify your hardening

# Check current SSH config:
sudo sshd -T | grep -E 'passwordauthentication|permitrootlogin|pubkeyauthentication|maxauthtries'

# Check fail2ban is active:
sudo fail2ban-client status sshd

# Check who's been blocked:
sudo fail2ban-client status sshd | grep "Banned IP"

# Check for recent login attempts in the log:
sudo journalctl -u sshd --since "1 hour ago" | grep -i "failed|invalid"

The complete sshd_config changes summary

# Changes to make in /etc/ssh/sshd_config:
PasswordAuthentication no
PubkeyAuthentication yes
PermitRootLogin no
MaxAuthTries 3
MaxSessions 5
LoginGraceTime 30
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no
PrintMotd no

Audit your UFW firewall rules alongside SSH hardening — check for Docker bypass risk, missing default-deny, and open ports that shouldn't be public.

Open Firewall Auditor →

Related guides