DevPlainly
Self-Hosting

How to Secure a New Linux VPS: 10 Steps for the First Hour

A practical hardening checklist for a fresh Ubuntu or Debian server: a sudo user, SSH keys, firewall, automatic updates, fail2ban and backups.

· 6 min read

Contents
  1. Before you start
  2. Step 1: Update the system
  3. Step 2: Create a regular user with sudo
  4. Step 3: Set up SSH key login for the new user
  5. Step 4: Lock down SSH
  6. Should you change the SSH port?
  7. Step 5: Turn on the firewall
  8. Step 6: Enable automatic security updates
  9. Step 7: Install fail2ban
  10. Step 8: Set the time zone and hostname
  11. Step 9: Add swap on small servers
  12. Step 10: Plan backups before you need them
  13. Quick checklist
  14. What to do next
  15. FAQ
  16. Is a VPS secure by default?
  17. Do I need fail2ban if I only allow SSH keys?
  18. I locked myself out. What now?
  19. Further reading

Bots start probing a new server within minutes of it getting a public IP address. Most of them try default passwords over SSH, so a few basic steps stop nearly all automated attacks.

This checklist is written for Ubuntu 22.04/24.04 and Debian 12, the most common VPS images. It takes about an hour, and you only do it once per server.

Before you start

You need the server's IP address and the root password or SSH key your provider gave you. Throughout this guide, replace 203.0.113.10 with your IP and deploy with the username you want.

Golden rule: whenever you change SSH or the firewall, keep your current session open and test the change from a second terminal. If something is wrong, you can still fix it from the first one. Most providers also offer a web console as a last resort.

Step 1: Update the system

ssh root@203.0.113.10
apt update && apt upgrade -y

If the upgrade installs a new kernel, reboot with reboot and log in again.

Step 2: Create a regular user with sudo

Working as root all the time makes every mistake catastrophic. Create a normal user:

adduser deploy
usermod -aG sudo deploy

Choose a strong password — you will use it for sudo, not for logging in.

Step 3: Set up SSH key login for the new user

From your own computer (not the server), copy your public key:

ssh-copy-id deploy@203.0.113.10

No SSH key yet? Our SSH keys guide walks through creating one on Linux, macOS and Windows.

Test it in a new terminal:

ssh deploy@203.0.113.10
sudo whoami     # should print: root

Step 4: Lock down SSH

Disable password logins and direct root login. Rather than editing the main file, create a drop-in file — it survives package updates:

sudo nano /etc/ssh/sshd_config.d/99-hardening.conf
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
MaxAuthTries 3

Watch out for other drop-in files. Many cloud images ship a file such as /etc/ssh/sshd_config.d/50-cloud-init.conf containing PasswordAuthentication yes. SSH uses the first value it reads for most settings, and files are read in alphabetical order, so that file can override yours. Check:

grep -r PasswordAuthentication /etc/ssh/sshd_config /etc/ssh/sshd_config.d/

Remove or edit any conflicting line, then validate the config and reload:

sudo sshd -t && sudo systemctl reload ssh

Confirm the effective settings:

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

From a second terminal, check that ssh deploy@203.0.113.10 still works and that ssh root@203.0.113.10 is refused.

Should you change the SSH port?

Moving SSH off port 22 reduces log noise from bots, but it is not real security — key-only authentication is. If you do change it, remember that recent Ubuntu versions start SSH through systemd socket activation, so a Port change in the config also needs sudo systemctl daemon-reload && sudo systemctl restart ssh.socket. Open the new port in the firewall before switching.

Step 5: Turn on the firewall

Ubuntu includes ufw (Uncomplicated Firewall). On Debian, install it with sudo apt install ufw.

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH          # or: sudo ufw allow 22/tcp
sudo ufw allow 80,443/tcp       # only if this server will host websites
sudo ufw enable
sudo ufw status verbose

Allow SSH before enabling the firewall, or you will lock yourself out.

If you use Docker: ports published with -p or a Compose ports: section are added to iptables directly and can bypass ufw rules. Publish only what must be public, and bind internal services to localhost, for example "127.0.0.1:5432:5432". More in our Docker Compose guide.

Step 6: Enable automatic security updates

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

Choose "Yes". Security patches are now installed daily. To reboot automatically at a quiet time when an update requires it, edit /etc/apt/apt.conf.d/50unattended-upgrades and set:

Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "04:00";

Step 7: Install fail2ban

fail2ban watches logs and temporarily bans IPs that fail to log in repeatedly. With password logins disabled it is less critical, but it cuts noise and protects other services you add later.

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

On Debian 12, which logs to the systemd journal instead of /var/log/auth.log, also add backend = systemd under [sshd].

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

Step 8: Set the time zone and hostname

Logs are easier to read with a sensible time zone. Many admins keep servers on UTC so scheduled jobs are not affected by daylight saving time (see cron and time zones).

sudo timedatectl set-timezone UTC
sudo hostnamectl set-hostname web-01

Step 9: Add swap on small servers

A 1 GB VPS can run out of memory during package installs or Docker builds. A swap file gives you breathing room:

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -h

Step 10: Plan backups before you need them

Security also means being able to recover. At minimum:

  • Enable your provider's snapshots or backups for the whole server. It usually costs a small percentage of the server price.
  • Back up application data separately (database dumps, uploaded files) to a different provider or location.
  • Test a restore once. A backup you have never restored is a hope, not a backup.

Quick checklist

DoneStep
☐System updated
☐Non-root sudo user created
☐SSH key login works for that user
☐Password and root SSH logins disabled (checked with sshd -T)
☐ufw enabled with only needed ports open
☐Automatic security updates enabled
☐fail2ban running
☐Time zone and hostname set
☐Swap configured (small servers)
☐Backups enabled and a restore tested

What to do next

  • Put a reverse proxy with automatic HTTPS in front of your apps — see how to set up Caddy as a reverse proxy.
  • Check what is listening on the network with sudo ss -tulpn. Anything you do not recognize deserves a look.
  • Review logins occasionally with last and sudo journalctl -u ssh --since today.

FAQ

Is a VPS secure by default?

Not fully. Providers give you a working system, but hardening — SSH keys, firewall, updates — is your responsibility.

Do I need fail2ban if I only allow SSH keys?

Bots cannot get in with password guessing once passwords are disabled, so it is optional for SSH. It still reduces log noise and helps protect other services.

I locked myself out. What now?

Use your provider's web or recovery console, which works even when SSH does not. From there, revert the last SSH or firewall change.

Further reading

Keep reading