Server Security Hardening: The Checklist I Run on Every New VPS
The exact server security hardening checklist for new Linux VPS deployments — SSH hardening, firewall setup, automatic updates, and intrusion detection basics.

James Ross Jr.
Strategic Systems Architect & Enterprise Software Developer
Server Security Hardening: The Checklist I Run on Every New VPS
Every new VPS I spin up gets hardened within the first thirty minutes of existence. This is not paranoia — it is operational hygiene. A fresh Ubuntu server on a public IP is being port-scanned within seconds of assignment. Automated bots are attempting SSH authentication within minutes. Skipping basic hardening is not a risk you defer; it is a risk you accept immediately.
Here is the exact checklist, in order, with the commands.
Step 1: System Updates
Before anything else, update all packages:
apt update && apt upgrade -y
apt install -y unattended-upgrades apt-listchanges
dpkg-reconfigure -plow unattended-upgrades
The unattended-upgrades package automatically applies security updates. Enable it. Configure automatic reboot in /etc/apt/apt.conf.d/50unattended-upgrades if your application can handle periodic restarts:
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "02:00";
Security patches that require a reboot will apply automatically at 2am. For applications that need zero-downtime patching, this needs more thought — but for most single-server deployments, it is the right tradeoff.
Step 2: Create a Non-Root User
Never operate as root. Create a dedicated admin user:
adduser james
usermod -aG sudo james
Copy your SSH keys to the new user:
rsync --archive --chown=james:james ~/.ssh /home/james
From this point, all work happens as the james user with sudo when needed.
Step 3: Harden SSH
This is the highest-leverage security step. The default SSH configuration accepts password authentication, which means the bot attacks scanning port 22 can potentially brute-force your server if you use a weak password.
Edit /etc/ssh/sshd_config:
# Disable root login
PermitRootLogin no
# Disable password authentication - require SSH keys
PasswordAuthentication no
PubkeyAuthentication yes
# Disable X11 forwarding if not needed
X11Forwarding no
# Disable empty passwords
PermitEmptyPasswords no
# Set max authentication attempts
MaxAuthTries 3
# Limit SSH access to your user
AllowUsers james
# Change port (optional - reduces log noise but is not true security)
# Port 2222
# Set idle timeout
ClientAliveInterval 300
ClientAliveCountMax 2
Verify your SSH key works before restarting sshd. Open a second terminal and test login before closing your current session.
sshd -t # Syntax check
systemctl restart sshd
If you change the SSH port, update your firewall rule before restarting. Getting locked out of a VPS is recoverable (most providers offer console access) but annoying.
Step 4: Configure UFW Firewall
UFW (Uncomplicated Firewall) is a front-end for iptables that makes firewall rules manageable:
apt install -y ufw
# Default: deny incoming, allow outgoing
ufw default deny incoming
ufw default allow outgoing
# Allow SSH (use 2222 if you changed the port)
ufw allow 22/tcp
# Allow HTTP and HTTPS
ufw allow 80/tcp
ufw allow 443/tcp
# Enable the firewall
ufw enable
# Verify rules
ufw status verbose
If you changed your SSH port, allow that port before enabling UFW — ufw enable with SSH blocked is another way to lock yourself out.
For servers that should only be accessed from specific IP ranges (an internal API server, for example), restrict access:
# Allow SSH only from your office IP
ufw allow from 203.0.113.50 to any port 22
# Deny SSH from everywhere else
ufw deny 22/tcp
Step 5: Fail2Ban
Fail2Ban monitors log files for repeated authentication failures and automatically bans the offending IP via firewall rules. It dramatically reduces brute-force attack surface:
apt install -y fail2ban
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Edit /etc/fail2ban/jail.local:
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
[sshd]
enabled = true
port = 22
maxretry = 3
bantime = 86400
This bans IPs that fail SSH authentication 3 times within 10 minutes, for 24 hours. Start Fail2Ban:
systemctl enable fail2ban
systemctl start fail2ban
Check ban status: fail2ban-client status sshd
Step 6: Disable Unused Services
Every running service is an attack surface. Check what is listening:
ss -tlnp
Review each open port. Disable services you do not need. On a fresh Ubuntu install, common candidates to disable:
# Disable postfix if you're not using the server as a mail relay
systemctl disable postfix
systemctl stop postfix
# Disable snapd if you don't use snap packages
systemctl disable snapd
The goal is minimum viable attack surface. Every port that is not open is a port that cannot be exploited.
Step 7: Kernel Hardening via sysctl
A few kernel parameters that improve security. Add these to /etc/sysctl.d/99-security.conf:
# Ignore ICMP broadcast requests (Smurf attack prevention)
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Disable source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
# Enable SYN flood protection
net.ipv4.tcp_syncookies = 1
# Log martian packets (spoofed source addresses)
net.ipv4.conf.all.log_martians = 1
# Disable ICMP redirect acceptance
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
# Protect against time-wait assassination
net.ipv4.tcp_rfc1337 = 1
Apply: sysctl -p /etc/sysctl.d/99-security.conf
Step 8: Audit Logging
Ensure system events are being logged and logs are sent offsite. Logs stored only on the compromised server are meaningless — an attacker with root access can delete them.
Install auditd for kernel-level audit logging:
apt install -y auditd
systemctl enable auditd
systemctl start auditd
Configure audit rules in /etc/audit/rules.d/audit.rules:
# Log authentication events
-w /var/log/auth.log -p rwxa -k auth
# Log user/group changes
-w /etc/passwd -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/shadow -p wa -k identity
# Log sudo usage
-w /var/log/sudo.log -p w -k sudo
# Log SSH key changes
-w /home -p w -k ssh_keys
Ship logs offsite to a SIEM or at minimum a log management service. Your auth logs on a compromised server cannot be trusted.
Step 9: Intrusion Detection with AIDE
AIDE (Advanced Intrusion Detection Environment) creates a database of file hashes for your system and alerts when files change unexpectedly. This detects rootkits and post-exploitation file modifications:
apt install -y aide
aideinit
cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db
Run checks daily via cron:
echo "0 4 * * * root /usr/bin/aide --check | mail -s 'AIDE Report' admin@yourdomain.com" >> /etc/crontab
The first few runs require tuning to mark expected changes (package updates, log rotations) as noise. After that, unexpected changes in system files are a serious alert.
The Ongoing Maintenance
Hardening is not a one-time event. Run apt upgrade regularly (or let unattended-upgrades handle it). Review Fail2Ban logs weekly. Audit active user accounts and SSH authorized keys quarterly — remove access for anyone who no longer needs it. When a vulnerability is announced in software you run, patch within 24 hours for critical severity.
Security is operational discipline, not a configuration you set and forget.
If you want a security review of your server infrastructure or help setting up a hardening baseline for your team, book a session at https://calendly.com/jamesrossjr.