Before you start

This guide assumes you've just provisioned a fresh Ubuntu 24.04 LTS VPS — whether through DS Webhosting's VPS plans or another provider — and you have root SSH access. The steps below are ordered: each one builds on the last, so work through them in sequence.

This is not an exhaustive server security guide. It's the minimum viable hardening I do on every new server before anything else goes on it. How much further you go depends on what the server is running and your threat model.

Do not lock yourself out. Before disabling password authentication or closing port 22, verify your SSH key is working in a separate terminal session. Test everything while you still have a fallback. Most providers also offer an emergency console if you do get locked out — know where it is before you start.

Step 1 — Update everything first

Do this before anything else. You want a clean baseline, and you want all currently-known vulnerabilities patched before the server is exposed to anything further.

# Update package lists and upgrade all installed packages
apt update && apt upgrade -y

# Also upgrade the kernel and any held-back packages
apt full-upgrade -y

# Clean up
apt autoremove -y && apt autoclean

Reboot after a kernel upgrade before continuing.

Step 2 — Create a non-root admin user

Running everything as root is bad practice. Create a dedicated admin user, give it sudo access, then lock down root login entirely.

# Create a new user — replace 'admin' with whatever you prefer
adduser admin

# Add to the sudo group
usermod -aG sudo admin

Now copy your SSH public key to the new user before you do anything else:

# Run this from your local machine, not the server
ssh-copy-id admin@your-server-ip

Open a new terminal and test that you can SSH in as the new user before proceeding. Don't close your root session yet.

Step 3 — Harden SSH

The SSH daemon configuration lives at /etc/ssh/sshd_config. Edit it carefully — a syntax error here can lock you out.

nano /etc/ssh/sshd_config

Find and set (or add) the following lines:

# Disable root login entirely
PermitRootLogin no

# Disable password authentication — keys only
PasswordAuthentication no
KbdInteractiveAuthentication no

# Only allow our admin user
AllowUsers admin

# Reduce login grace period
LoginGraceTime 30

# Reduce max auth attempts
MaxAuthTries 3

# Disable unused authentication methods
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no

On port changes: Some guides recommend moving SSH off port 22. This does reduce noise in your logs and stops opportunistic scanners, but it's security through obscurity rather than actual security. I do it on servers where cleaner logs matter, but it's not a substitute for key-based auth and fail2ban. If you do change the port, remember to open it in UFW before restarting SSH.

Test the config for syntax errors, then restart:

sshd -t        # dry run — should return nothing if config is valid
systemctl restart ssh

With your root session still open, test logging in as your admin user in a new terminal. If it works, you can close the root session.

Step 4 — Configure UFW firewall

Ubuntu ships with UFW (Uncomplicated Firewall), which is a sensible frontend to iptables for most use cases.

# Deny all incoming by default, allow all outgoing
ufw default deny incoming
ufw default allow outgoing

# Allow SSH (adjust port number if you changed it)
ufw allow 22/tcp

# Allow HTTP and HTTPS if this is a web server
ufw allow 80/tcp
ufw allow 443/tcp

# Enable the firewall
ufw enable

# Verify rules
ufw status verbose

Only open ports that you actually need. Every open port is an attack surface. If you're not running a mail server, don't open port 25. If you're not running a database that needs remote access, don't open 3306.

Step 5 — Install and configure fail2ban

fail2ban monitors log files and automatically bans IPs that show brute-force behaviour. With key-based SSH auth and root login disabled, brute-forcing your SSH is already very hard — but fail2ban keeps log noise down and provides an extra layer.

apt install fail2ban -y

Create a local configuration file that won't be overwritten on package updates:

cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
nano /etc/fail2ban/jail.local

Find the [sshd] section and confirm or set:

[sshd]
enabled  = true
port     = ssh
maxretry = 3
bantime  = 1h
findtime = 10m

If you changed the SSH port, update the port value accordingly. Then start and enable the service:

systemctl enable fail2ban
systemctl start fail2ban
fail2ban-client status sshd   # verify it's watching

Step 6 — Enable unattended security upgrades

The biggest real-world security risk on most servers is not a sophisticated attack — it's an unpatched vulnerability that's been sitting there for six months because nobody got around to running updates. Unattended upgrades fixes that for security patches specifically.

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

Accept the prompt to enable automatic updates. The default configuration restricts automatic updates to security patches only, which is the right balance — you don't want full apt upgrade running automatically and potentially breaking things, but you do want CVE patches applied without manual intervention.

Check the configuration at /etc/apt/apt.conf.d/50unattended-upgrades — specifically that the Unattended-Upgrade::Allowed-Origins block includes the Ubuntu:24.04 security archive.

Step 7 — Set a hostname and timezone

A small thing that pays dividends when you're reading logs later, especially if you manage multiple servers.

# Set a meaningful hostname
hostnamectl set-hostname your-server-name

# Set timezone — adjust to yours
timedatectl set-timezone Australia/Melbourne

# Verify NTP is syncing (critical for log timestamps and SSL)
timedatectl status

Ubuntu 24.04 uses systemd-timesyncd for NTP by default, which is fine for most use cases. If it's not running, start it with systemctl enable --now systemd-timesyncd.

Step 8 — Review running services

Know what's running on your server. The attack surface is everything that's listening for connections.

# List all listening ports and the services bound to them
ss -tlnp

# List all active systemd services
systemctl list-units --type=service --state=active

Disable anything you don't need. Common candidates on a minimal install are snapd (if you're not using snaps) and any services installed as dependencies but not required by your workload.

# Example: disable and remove snapd if you don't need it
systemctl disable --now snapd
apt purge snapd -y

Step 9 — Configure automatic log rotation

Ubuntu handles this via logrotate, which is typically installed and configured by default. Verify it's set up:

logrotate --debug /etc/logrotate.conf   # dry run

For servers generating significant log volume (busy web servers, mail servers), review /etc/logrotate.d/ and ensure rotation frequency and retention period make sense for your storage constraints.

Optional — worth considering for most servers

auditd — system call auditing

For servers handling sensitive data or in regulated environments, auditd provides detailed logging of system calls, file access, and privilege escalation. More overhead, but invaluable for incident investigation.

apt install auditd audispd-plugins -y
systemctl enable --now auditd

rkhunter — rootkit detection

A periodic rootkit scanner. Not a substitute for good security practices, but useful as a canary. Run it after initial setup to establish a clean baseline, then schedule it as a cron job.

apt install rkhunter -y
rkhunter --update
rkhunter --propupd    # set baseline
rkhunter --check      # initial scan

2FA for sudo

For high-value servers, adding TOTP second-factor authentication to sudo via libpam-google-authenticator adds a meaningful barrier against compromised admin credentials.

The quick-reference checklist

Ubuntu 24.04 VPS hardening — minimum baseline

  • apt update && apt full-upgrade -y — all packages current
  • Non-root admin user created with sudo access
  • SSH public key installed for admin user
  • SSH: PermitRootLogin no
  • SSH: PasswordAuthentication no
  • SSH: AllowUsers set to your admin user
  • SSH config tested with sshd -t before restarting
  • UFW enabled with default deny incoming
  • UFW: only required ports open
  • fail2ban installed and watching sshd
  • Unattended upgrades enabled for security patches
  • Hostname and timezone set correctly
  • Running services reviewed — unnecessary ones disabled

This is the floor, not the ceiling. What goes on top of this baseline depends on your workload — a public-facing web server, a private development environment, and a database server all have different requirements from here. If you're not sure where to take it from here for your specific setup, get in touch.

Need someone to manage your VPS?

If you'd rather have an experienced Linux administrator handle this — and keep it maintained over time — that's exactly what DS Webhosting Services provides.

View server management services
← All resources Next: AI development explained →