Server hardening basics: what I do on a fresh VPS

A fresh VPS from any provider arrives with the same defaults: root login over password authentication, SSH on port 22, and every port you asked for open. The first hour determines most of your exposure, because scanners find new servers within minutes and start trying passwords immediately. The checklist below is what I run on every box I provision. None of it is complicated; it is mostly turning off defaults and writing down the order of operations.

SSH: keys only, root off

The first change is SSH. Generate an ed25519 key locally, copy it to the server, and verify you can log in with it before changing anything. Then set PasswordAuthentication no, PermitRootLogin no, and PubkeyAuthentication yes in sshd_config, test the configuration with sshd -t, and restart sshd only while a second session is open so a mistake does not lock you out. Changing the port is optional and mostly reduces log noise; it does not meaningfully improve security, and it complicates every client. Keep the key passphrase-protected locally and treat the private key like the credential it is.

Firewall and exposed ports

Default-deny is the rule: allow only what the box exists to serve. With UFW, that means incoming SSH and 80/443 if the server runs web traffic, and everything else rejected. Re-check after every install, because package managers happily open ports as a side effect. Run fail2ban or an equivalent to jail repeated SSH failures, and check the logs periodically — a box that exists but is never looked at will be compromised quietly.

Updates and patching

Unpatched software is the most reliable way to get compromised, so updates are non-negotiable. Enable unattended-upgrades for security patches, and have a plan for reboots, since kernel patches do not apply until you restart. If the box runs long-lived services, schedule the reboot window instead of hoping an uptime streak matters. Patching is not glamorous, but it closes more holes than any single hardening step.

Users and least privilege

Create a normal user with sudo instead of working as root. Root should have no login path at all — no password, no key — so every administrative action is attributable to a named user. Avoid passwordless sudo and wildcard sudoers entries; the friction of typing a password is the point. Remove or disable any default users the provider created, and never share accounts between people.

Secrets on the server

Servers accumulate credentials fast: database passwords, API keys, and tokens in config files and environment files. Keep them out of repositories entirely, store them in a password store with 0600 permissions, and pass them to processes through environment variables rather than command lines. For agent workloads that need many credentials, a credential broker that reads an existing pass or Bitwarden store and injects secrets into a subprocess environment removes most of the manual ceremony — that is the problem the trustless project solves, and its source is at https://github.com/ikkun1222/trustless.

Logs, monitoring, and backups

Finally, make the box observable. journald with logrotate keeps logs bounded and reviewable; a monitoring check on disk, memory, and process state catches problems early. Set the clock with chrony, add swap if the workload needs it, and back up anything irreplaceable to a different machine. A server that fails is normal; a server that fails with no backup and no logs is a disaster.

The list is short and mostly boring, which is the point. If you provisioned a new VPS today, which step would you skip — and what would that cost you in a month?