Why Initial Setup Matters
Cloud servers start receiving SSH brute-force attacks the moment their IP becomes public. Leave the defaults in place and hundreds of login attempts pile up within minutes.
# Check actual login attempts on a new server
sudo journalctl -u ssh --since "1 hour ago" | grep "Failed password" | wc -lFollow the steps below in order and you can block most automated attacks.
Step 1 — Update Packages
This is the first thing you should do as soon as you get the server.
sudo apt update && sudo apt upgrade -y
# Remove unnecessary packages
sudo apt autoremove -y && sudo apt autoclean
# Check whether a reboot is required
cat /var/run/reboot-required 2>/dev/null && echo "Reboot required" || echo "Reboot not required"
# Reboot if needed
sudo rebootStep 2 — Create a Non-Root Admin Account
Working directly as root is risky. Create a dedicated admin account and disable direct root login.
# Create a new user
sudo adduser deploy
# Grant sudo privileges
sudo usermod -aG sudo deploy
# Switch users and verify sudo works
su - deploy
sudo whoami # should print rootStep 3 — Set Up SSH Key Authentication
Using SSH key authentication instead of passwords blocks brute-force attacks at the source.
Generate a Key Pair on Your PC
# Run this on your local PC
ssh-keygen -t ed25519 -C "my-server"
# Save path: ~/.ssh/id_ed25519 (press Enter for the default)
# Passphrase: recommended
# View the public key
cat ~/.ssh/id_ed25519.pubRegister the Public Key on the Server
# Method 1: ssh-copy-id (easiest)
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@SERVER_IP
# Method 2: manual registration (on the server)
mkdir -p ~/.ssh && chmod 700 ~/.ssh
echo "PASTE_PUBLIC_KEY_HERE" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keysVerify Key Authentication Works
# In a new terminal, test connecting with the key (should connect without a password)
ssh -i ~/.ssh/id_ed25519 deploy@SERVER_IPAlways confirm that key-based login succeeds before moving on. If you disable password authentication first, you will lock yourself out of the server.
Step 4 — Harden SSH Settings
sudo nano /etc/ssh/sshd_configFind and update the following items.
# Block direct root login
PermitRootLogin no
# Disable password authentication (only after confirming key login!)
PasswordAuthentication no
# Disallow empty passwords
PermitEmptyPasswords no
# Limit authentication attempts
MaxAuthTries 3
# Limit login wait time (30 seconds)
LoginGraceTime 30
# Explicitly allow users (optional)
AllowUsers deploy# Check config syntax
sudo sshd -t
# Apply if there are no issues
sudo systemctl reload ssh
# Keep the current session open and test from a new terminal
ssh deploy@SERVER_IPStep 5 — Configure the UFW Firewall
Open only the ports you need and block everything else.
# Confirm UFW is installed
sudo apt install -y ufw
# Default policy: deny incoming, allow outgoing
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH (do this first so you don't drop your current session!)
sudo ufw allow ssh # port 22
# Add these if you run a web server
sudo ufw allow http # 80
sudo ufw allow https # 443
# Enable UFW
sudo ufw enable # type y
# Check status
sudo ufw status verboseAlways allow SSH (port 22) before enabling UFW. Otherwise you will lose access to the server.
Step 6 — Install fail2ban
Automatically ban IPs that fail login repeatedly.
sudo apt install -y fail2ban
# Create a local config file (do not edit the original directly)
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local[DEFAULT]
# Ban for 1 hour after 5 failures within 10 minutes
findtime = 600
maxretry = 5
bantime = 3600
[sshd]
enabled = true
port = sshsudo systemctl enable --now fail2ban
# Check ban status
sudo fail2ban-client status sshd
# Unban a specific IP
sudo fail2ban-client set sshd unbanip 1.2.3.4Step 7 — Enable Automatic Security Updates
If you can't keep up with patches manually, install security updates automatically.
sudo apt install -y unattended-upgrades
# Review and enable the configuration
sudo dpkg-reconfigure -plow unattended-upgrades
# Check the config file
cat /etc/apt/apt.conf.d/20auto-upgrades# /etc/apt/apt.conf.d/20auto-upgrades
APT::Periodic::Update-Package-Lists "1"; # refresh lists daily
APT::Periodic::Unattended-Upgrade "1"; # install security updates daily
APT::Periodic::AutocleanInterval "7"; # clean cache every 7 daysStep 8 — Final Verification Checklist
# Confirm only SSH key authentication is allowed
sudo sshd -T | grep -E "permitrootlogin|passwordauthentication|maxauthtries"
# Firewall status
sudo ufw status verbose
# Whether fail2ban is running
sudo systemctl is-active fail2ban
# Whether automatic updates are enabled
cat /etc/apt/apt.conf.d/20auto-upgrades
# Recent login history
last | head -10Expected results after setup:
| Item | Expected result |
|---|---|
| Root SSH login | Blocked |
| Password login | Blocked |
| Firewall | SSH/HTTP/HTTPS only |
| fail2ban | Ban for 1 hour after 5 failures |
| Security updates | Applying automatically |
Next Steps — Deeper Guides for Each Topic
This guide focused on covering everything without gaps. For a deeper dive into each item, see the guides below.
| Topic | Guide | Difficulty |
|---|---|---|
| sshd_config hardening, key auth, AllowGroups, MFA | Hardening SSH: Locking Down sshd_config with Key-Only Login, AllowGroups, and MFA | Intermediate |
| fail2ban jails, recidive permanent bans, email, Cloudflare | Blocking SSH Brute-Force with fail2ban | Intermediate |
| UFW port ranges, app profiles, logging | Complete UFW Firewall Setup Guide | Beginner |
Role of this guide: A starting point so you don't miss anything when you first receive a server. In production, you should also apply the deeper guides above.
AI tools may assist drafting. Check the evidence in each guide for whether commands were run and which environment was used. Confirm the current docs for your version before applying instructions, and email us about errors. email us
Questions & answers
Ask a question about this guide. We'll review and reply.