VPS Hosting

Securing a Fresh Ubuntu VPS: SSH Keys, UFW and Fail2ban

Lock down a new Ubuntu VPS step by step — SSH keys, disabling passwords and root login, UFW firewall rules, Fail2ban, automatic updates, and the Docker firewall gotcha.

On this page
  1. Step 1: Use SSH keys, not passwords
  2. Step 2: Disable password and root login
  3. Step 3: Enable the UFW firewall
  4. Step 4: Install Fail2ban
  5. Step 5: Keep the system patched
  6. Step 6: Reduce what’s exposed
  7. Step 7: Don’t forget IPv6
  8. Beyond the basics
  9. A quick audit
  10. Summary

Within minutes of a server getting a public IP address, automated bots start trying to log in. Most of these attacks are crude — guessing common passwords for common usernames — and a few simple measures make them hopeless. This guide covers the essentials for an Ubuntu VPS: SSH keys, a firewall, Fail2ban and automatic updates.

Step 1: Use SSH keys, not passwords

An SSH key pair consists of a private key (kept on your computer) and a public key (placed on the server). Keys are far too long to guess, which makes brute-force attacks pointless.

On your computer:

ssh-keygen -t ed25519 -C "laptop"

Protect the key with a passphrase when prompted. Then copy the public key to your server’s user:

ssh-copy-id deploy@YOUR_SERVER_IP

If ssh-copy-id isn’t available (for example on Windows without WSL), append the contents of your .pub file to ~/.ssh/authorized_keys on the server and make sure permissions are strict: chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys.

Test the key login in a new terminal before changing anything else.

Step 2: Disable password and root login

Edit /etc/ssh/sshd_config:

PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
PubkeyAuthentication yes

The override trap

Ubuntu reads extra configuration from /etc/ssh/sshd_config.d/*.conf, and settings there are read before the main file — the first value OpenSSH finds for most options wins. Some cloud images ship a file such as 50-cloud-init.conf containing PasswordAuthentication yes, which silently keeps passwords enabled no matter what you put in sshd_config.

Check:

sudo grep -ri passwordauthentication /etc/ssh/sshd_config.d/

Edit or remove any line that enables passwords. Then validate the configuration and restart:

sudo sshd -t && sudo systemctl restart ssh

sshd -t checks for syntax errors, so a typo can’t lock you out. Confirm the effective settings:

sudo sshd -T | grep -E 'passwordauthentication|permitrootlogin'

Keep an existing session open while you test a fresh login. If something goes wrong, your provider’s web VNC console (included with Kerit Cloud VPS plans) lets you fix it without SSH.

Step 3: Enable the UFW firewall

UFW (Uncomplicated Firewall) ships with Ubuntu. The safe order is: set defaults, allow SSH, then enable.

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw enable

Open only what you run:

sudo ufw allow 80,443/tcp                                  # web server
sudo ufw allow from 203.0.113.10 to any port 3306 proto tcp  # database, one trusted IP only
sudo ufw status numbered
sudo ufw delete 3                                          # remove a rule by number

Restricting sensitive ports to specific IPs — databases, admin panels, monitoring — is one of the most effective protections available.

The Docker gotcha

Docker manipulates iptables directly when you publish container ports (-p 8080:80), and those rules take effect before UFW’s. A container port you think is blocked by UFW may be wide open to the internet.

The simplest fixes:

  • Publish ports on localhost only when a reverse proxy sits in front: -p 127.0.0.1:8080:80.
  • Don’t publish ports that only other containers need; use a Docker network instead.
  • Use your provider’s edge firewall as an outer layer.

See installing Docker and Docker Compose.

Step 4: Install Fail2ban

Even with passwords disabled, bots keep knocking and fill your logs. Fail2ban watches logs and temporarily bans IPs that fail repeatedly.

sudo apt install -y fail2ban

Create /etc/fail2ban/jail.local (never edit jail.conf, which updates overwrite):

[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
backend = systemd

[sshd]
enabled = true

Start it and check:

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

Fail2ban can also protect web servers and other services with additional jails.

Step 5: Keep the system patched

Most compromised servers weren’t hacked with clever exploits — they were running software with known, fixed vulnerabilities. Enable automatic security updates:

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

Reboot periodically for kernel updates. cat /var/run/reboot-required tells you when one is pending.

Step 6: Reduce what’s exposed

  • List listening services: sudo ss -tulpn. Anything listening on 0.0.0.0 or [::] is reachable from the internet unless the firewall blocks it.
  • Bind internal services to localhost. Databases, Redis and admin tools usually only need 127.0.0.1.
  • Remove what you don’t use. Every installed service is something that needs patching.

Step 7: Don’t forget IPv6

If your VPS has IPv6 — Kerit Cloud plans include a /64 subnet — make sure the firewall covers it. UFW handles IPv6 when IPV6=yes is set in /etc/default/ufw (the default on Ubuntu). A service blocked on IPv4 but open on IPv6 is still open.

Beyond the basics

Once these are in place, consider:

  • Changing the SSH port — reduces log noise, but isn’t real security on its own.
  • Restricting SSH to your IPs via UFW or your provider’s edge firewall.
  • Two-factor authentication for SSH, and short-lived keys for teams.

These are covered in hardening SSH beyond the basics. For network-level attacks, Kerit Cloud VPS plans include Cloudflare Magic Transit and OVH TCP Shield DDoS protection, plus an edge firewall — see using an edge firewall and port policies.

A quick audit

sudo sshd -T | grep -E 'passwordauthentication|permitrootlogin'   # both "no"
sudo ufw status verbose                                           # active, minimal rules
sudo fail2ban-client status sshd                                  # running
sudo ss -tulpn                                                    # only expected listeners
systemctl status unattended-upgrades --no-pager                   # active

Summary

Secure a new Ubuntu VPS by switching to SSH keys and disabling password and root login — checking sshd_config.d for overrides and validating with sshd -t. Enable UFW with default-deny and only the ports you need, restricting sensitive ones to trusted IPs, and remember that Docker’s published ports bypass UFW. Add Fail2ban for repeated failures, enable unattended security upgrades, bind internal services to localhost, and cover IPv6 as well as IPv4.