+91 80401 38000[email protected]24/7 Expert Support
[email protected]Client Portal →
ServerGurus
← All posts
HostingSecurityVPSLinuxUbuntu

VPS Security Hardening Checklist: 20 Steps to Lock Down a Production Server

By ServerGurus Team24 July 20266 min read
VPS Security Hardening Checklist: 20 Steps to Lock Down a Production Server

Introduction

You just provisioned a fresh VPS. It has a public IP address and a root password. Within minutes, bots will start scanning it. If you deploy an application before locking down the server, you are inviting trouble.

This checklist covers 20 steps to harden a Linux server from a fresh Ubuntu 24.04 install to a production-grade setup. Every step includes the exact commands. No theory, no fluff - just a secure server by the end of this page.

Prerequisites

  • Fresh Ubuntu 24.04 (works on 22.04 and Debian 12 with minor adjustments)
  • Root access
  • A non-root user you will create in Step 2

1. Update the System

apt update && apt upgrade -y

Do this first. Always. Patching known vulnerabilities is step zero of any security effort.

2. Create a Non-Root User

Never SSH as root. Create a user with sudo privileges:

adduser deploy
usermod -aG sudo deploy

Replace deploy with your preferred username.

3. SSH Key-Only Authentication

Disable password authentication. First, copy your public key:

# On your local machine:
ssh-copy-id -i ~/.ssh/id_ed25519 deploy@your-server-ip

Then edit /etc/ssh/sshd_config:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys

Test with a NEW terminal window before closing the current session:

ssh deploy@your-server-ip

If it connects, restart SSH:

systemctl restart sshd

4. Change the SSH Port

Move SSH off port 22 to reduce log noise from automated scanners:

# In /etc/ssh/sshd_config
Port 2222

Update your firewall (step 5) to allow the new port. Restart SSH:

systemctl restart sshd

Connect on the new port before closing your current session:

ssh -p 2222 deploy@your-server-ip

5. Configure UFW Firewall

ufw default deny incoming
ufw default allow outgoing
ufw allow 2222/tcp    # SSH (custom port)
ufw allow 80/tcp      # HTTP
ufw allow 443/tcp     # HTTPS
ufw enable
ufw status verbose

Only open ports your application actually needs. For a web server, ports 80 and 443 plus your SSH port are sufficient.

6. Install and Configure Fail2Ban

Fail2Ban blocks IPs after repeated failed login attempts:

apt install fail2ban -y

Create /etc/fail2ban/jail.local:

[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
ignoreip = 127.0.0.1/8 ::1

[sshd]
enabled = true
port = 2222
logpath = /var/log/auth.log

[nginx-http-auth]
enabled = true

[nginx-botsearch]
enabled = true

Start and enable:

systemctl enable fail2ban --now
fail2ban-client status sshd

7. Upgrade to CrowdSec (Recommended)

CrowdSec is the modern replacement for Fail2Ban. It uses community-shared blocklists and behavioral analysis. Install alongside or as a replacement:

curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | bash
apt install crowdsec crowdsec-firewall-bouncer-iptables -y
systemctl enable crowdsec --now

CrowdSec automatically detects and blocks SSH brute-force, HTTP attacks, and more. The free tier includes community blocklists updated hourly.

8. Disable Root Login and Unused Services

passwd -l root          # Lock root password
systemctl disable --now rpcbind 2>/dev/null
systemctl disable --now avahi-daemon 2>/dev/null
systemctl disable --now cups 2>/dev/null

List all running services and disable anything you do not need:

systemctl list-units --type=service --state=running

9. Enable Automatic Security Updates

apt install unattended-upgrades -y
dpkg-reconfigure -plow unattended-upgrades

Select "Yes" when prompted. This automatically installs security patches daily. Edit /etc/apt/apt.conf.d/50unattended-upgrades to enable automatic reboots if needed:

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

10. Configure Kernel Hardening via sysctl

Create /etc/sysctl.d/99-hardening.conf:

# IP spoofing protection
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Ignore ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0

# Ignore send redirects
net.ipv4.conf.all.send_redirects = 0

# Disable source packet routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0

# SYN flood protection
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 2

# Disable IP forwarding (unless you need routing)
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0

# Log Martian packets
net.ipv4.conf.all.log_martians = 1

Apply:

sysctl -p /etc/sysctl.d/99-hardening.conf

11. Secure Shared Memory

Add to /etc/fstab:

tmpfs /run/shm tmpfs defaults,noexec,nosuid 0 0

Mount it:

mount -o remount /run/shm

12. Set Up AppArmor

AppArmor is enabled by default on Ubuntu. Verify:

aa-status

Create custom profiles for your application if needed. At minimum, ensure the default profiles are loaded and enforcing.

13. Install and Configure auditd

Auditd logs system calls for forensic analysis:

apt install auditd -y
systemctl enable auditd --now

Add rules to /etc/audit/rules.d/audit.rules:

-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/sudoers -p wa -k sudoers
-w /var/log/auth.log -p wa -k authlog
-w /etc/ssh/sshd_config -p wa -k sshd_config

Restart auditd:

systemctl restart auditd

14. Disable IPv6 (If Not Used)

If your application does not need IPv6, disable it:

# In /etc/sysctl.conf
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1

Apply:

sysctl -p

15. Set Up a Firewall for Outbound Traffic

Restrict outbound connections to only what your server needs:

ufw default deny outgoing
ufw allow out 53         # DNS
ufw allow out 80/tcp     # HTTP (apt updates)
ufw allow out 443/tcp    # HTTPS (API calls, webhooks)
ufw allow out 123/udp    # NTP
ufw allow out 2222/tcp   # Your custom SSH port for outbound SSH

Add application-specific outbound rules (e.g., SMTP port 587 for email). Test thoroughly before deploying - outbound restrictions break package updates and external API calls.

16. Harden Nginx/Apache

If running a web server, add security headers. For Nginx, add to your server block:

add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# HSTS (only with valid SSL)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

Hide version information:

server_tokens off;

17. Set Up Monitoring

Install Node Exporter for Prometheus monitoring:

apt install prometheus-node-exporter -y
systemctl enable prometheus-node-exporter --now

Or use Netdata for a quick all-in-one dashboard:

curl https://get.netdata.cloud/kickstart.sh > /tmp/netdata-kickstart.sh
sh /tmp/netdata-kickstart.sh --stable-channel --disable-telemetry

Monitor CPU, memory, disk, network, and process activity. Alert on anomalies.

18. Enable Process Accounting

Process accounting logs every command executed:

apt install acct -y
systemctl enable acct --now

View command history:

lastcomm
sa

19. Set File Integrity Monitoring

Install AIDE (Advanced Intrusion Detection Environment):

apt install aide -y
aideinit
mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db

Run daily checks via cron:

# /etc/cron.daily/aide-check
#!/bin/bash
/usr/bin/aide --check | mail -s "AIDE Report $(hostname)" [email protected]

AIDE alerts you when critical system files change unexpectedly.

20. Regular Maintenance Checklist

Set up a monthly cron job:

#!/bin/bash
apt update && apt upgrade -y
rkhunter --check --skip-keypress
chkrootkit
aide --check
fail2ban-client status
systemctl list-units --state=failed
df -h
free -h

Save this as /root/bin/monthly-check.sh and run it on the first of every month.

How ServerGurus Helps

Every ServerGurus VPS ships with steps 1-10 pre-configured: SSH key-only auth, UFW with custom SSH port, Fail2Ban/CrowdSec, unattended upgrades, and kernel hardening. Our managed hosting plans add 24/7 monitoring, intrusion detection, and incident response. You focus on your application, we handle the rest.

For a VPS that is secure from the moment you log in, check our plans.

Ready to build your infrastructure?

Get a quote from our Hyderabad-based team - Tier IV datacenter, real support, INR or USD billing.

View pricingRequest a quoteWhatsApp sales