All systems operational
Support
EN
Language

More languages are on the way.

The first hour on a new Ubuntu VPS: a hardening checklist

Keys only, a firewall that defaults to deny, automatic patches and a few kernel knobs: the twelve steps we wish everyone did before deploying anything.

CSCheapServ SecurityWritten by 8 min read
A shield in front of a server with a key beside it
On this page13
  1. 0. Before you log in
  2. 1. Keys only, root locked
  3. 2. A firewall that defaults to deny
  4. 3. Automatic security patches
  5. 4. fail2ban for the long tail
  6. 5. Time, hostname, locale
  7. 6. A few kernel knobs
  8. 7. Know what is listening
  9. 8. Users and sudo
  10. 9. Two-factor on the panel, not on SSH
  11. 10. A backup before the first deploy
  12. 11. Something that tells you when it breaks
  13. 12. Ten-minute review, then deploy

A fresh server on a public IP receives its first SSH brute-force attempt within about four minutes, on average, across our fleet. Most of what follows is not sophisticated: password guessing, scanners for old panels, bots looking for an open Redis. This checklist is the hour we recommend spending before deploying anything, in the order that matters, on Ubuntu 24.04. Every step applies to Debian with trivial changes.

0. Before you log in

Add your SSH public key in the panel under SSH keys before you order. Servers deployed with a key have password authentication disabled from the first boot, which makes step one a check rather than a change. If you deployed with a password instead, do steps one and two immediately, in the same session.

1. Keys only, root locked

Create a user, give it your key, and turn password authentication off for everyone.

adduser --disabled-password --gecos "" ops
usermod -aG sudo ops
mkdir -p /home/ops/.ssh && cp ~/.ssh/authorized_keys /home/ops/.ssh/
chown -R ops:ops /home/ops/.ssh && chmod 700 /home/ops/.ssh
cat > /etc/ssh/sshd_config.d/10-hardening.conf <<'EOF'
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
MaxAuthTries 3
LoginGraceTime 20
EOF
sshd -t && systemctl reload ssh

Open a second terminal and confirm you can log in as ops with your key before closing the first one. The drop-in directory is the point: Ubuntu updates never overwrite it, unlike edits to sshd_config itself.

2. A firewall that defaults to deny

Three rules cover most servers: allow SSH, allow HTTP and HTTPS, deny everything else. nftables is already installed; ufw is a fine front end for it.

ufw default deny incoming
ufw default allow outgoing
ufw limit 22/tcp comment 'ssh, rate limited'
ufw allow 80,443/tcp comment 'web'
ufw enable

limit instead of allow on port 22 drops an address after six connection attempts in thirty seconds, which takes the edge off brute force before fail2ban even starts. Remember that Docker publishes ports around ufw by default; if you run containers, either bind them to 127.0.0.1 and put a reverse proxy in front, or set "iptables": false in /etc/docker/daemon.json and manage the rules yourself.

3. Automatic security patches

Unattended upgrades are installed on our images but not always enabled. Turn them on and give the machine a reboot window so kernel updates actually apply.

apt install -y unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades
cat > /etc/apt/apt.conf.d/52reboot <<'EOF'
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "04:30";
EOF

If a nightly reboot is unacceptable, leave the reboot off and subscribe to the Ubuntu security list instead. What is not acceptable is a server that nobody patches; that is how most of the abuse reports we receive begin.

4. fail2ban for the long tail

apt install -y fail2ban
cat > /etc/fail2ban/jail.local <<'EOF'
[DEFAULT]
bantime  = 1h
findtime = 10m
maxretry = 5
backend  = systemd
[sshd]
enabled = true
EOF
systemctl enable --now fail2ban

With keys only, fail2ban is not protecting the login so much as keeping the log readable. The systemd backend matters on 24.04 because /var/log/auth.log is no longer written by default.

5. Time, hostname, locale

Certificates, logs and two-factor codes all depend on a correct clock. Our images use systemd-timesyncd against our own stratum-2 servers; check with timedatectl that System clock synchronized: yes. Set the hostname you used in the panel with hostnamectl set-hostname web-01 so that reverse DNS, mail and monitoring agree.

6. A few kernel knobs

cat > /etc/sysctl.d/60-hardening.conf <<'EOF'
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.tcp_syncookies = 1
kernel.kptr_restrict = 2
kernel.dmesg_restrict = 1
fs.protected_symlinks = 1
fs.protected_hardlinks = 1
EOF
sysctl --system

These are the defaults on hardened distributions and cost nothing. Do not copy random “performance” sysctls from forums; most of them predate the current kernel and some make things worse.

7. Know what is listening

ss -tulpn lists every listening socket. On a fresh image it should show sshd on 22 and systemd-resolved on 127.0.0.53, nothing else. Every service you add later should either be bound to localhost or have a firewall rule you can explain. Redis, MongoDB, Elasticsearch and Docker's API have all shipped, at various points, listening on all interfaces with no authentication; each of those has produced a data breach for somebody.

8. Users and sudo

One person, one account. Shared root passwords are how you lose track of who deleted what. Require a password for sudo (the default) and, if several people administer the box, set Defaults logfile=/var/log/sudo.log in a file under /etc/sudoers.d/ so that privileged commands are recorded.

9. Two-factor on the panel, not on SSH

Enable TOTP two-factor authentication on your CheapServ account: the panel can reinstall your server, open a console and change the root password, which makes it the more valuable target. SSH with a hardware-backed key (a YubiKey via ssh-keygen -t ed25519-sk) is stronger than TOTP on SSH and simpler to operate.

10. A backup before the first deploy

Take a snapshot in the panel now, while the system is clean and known-good. It is the fastest rollback you will ever have. Then set up an off-site backup for the data you will create; we wrote a separate guide with a restic recipe that costs a few dollars a month.

11. Something that tells you when it breaks

At minimum, a free uptime monitor hitting your site every minute and emailing you. Better, netdata (one of our add-ons) for a live view of CPU, disk and network, and a log shipper if you have more than one server. You will not check dashboards; you need the alert.

12. Ten-minute review, then deploy

  • ssh ops@server works with a key; ssh root@server with a password is refused.
  • ufw status verbose shows deny by default with your three rules.
  • systemctl status unattended-upgrades fail2ban are both active.
  • ss -tulpn shows nothing you cannot name.
  • A snapshot exists and two-factor is on in the panel.

That is the hour. Everything after it, from the reverse proxy to the database, sits on a base that will not be the reason you get a phone call at 3 a.m. If you would rather not do it by hand, the fail2ban, Automatic security updates and Tailscale add-ons in the order form apply steps 3, 4 and a private management path during provisioning.

CS
CheapServ Security

Handles abuse, incident response and the defaults every image ships with.

Deploy your first server in under a minute.

Top up from $25 in BTC, ETH, XMR or USDT. Your balance never expires and unused funds are refundable.

Sign up now