A freshly deployed VPS comes with dangerous defaults: root SSH login enabled, password-based authentication active, no firewall configured. Bots begin scanning your IP within minutes of deployment. Server hardening is the systematic process of eliminating these vulnerabilities before your first legitimate user reaches the server. The good news: you can complete a comprehensive hardening in under 30 minutes, and the protection lasts for years.
Why Server Hardening Is Critical
The statistics are sobering. Security researchers monitoring unprotected VPS instances regularly observe thousands of login attempts per day within hours of deployment. Security researchers have observed that unprotected servers are typically breached within days of deployment. These aren't sophisticated targeted attacks — they're automated scanning and brute-force attempts by compromised bot networks constantly probing the internet.
The cost of a breached VPS extends beyond data loss. A compromised server becomes a vector for attacking other systems, sending spam emails, hosting malware, or joining a botnet. Your hosting provider may suspend your account, damage your reputation, and leave you liable for downstream harm.
Hardening addresses four fundamental security goals:
- Reduce attack surface — Close unused ports, disable unnecessary services, limit network exposure
- Prevent unauthorized access — Use SSH keys instead of passwords, disable root login, implement rate limiting with Fail2Ban
- Stay patched automatically — Enable unattended security updates so critical vulnerabilities never linger
- Apply least-privilege principle — Run services with the minimum necessary permissions, use sudo instead of direct root access
Step 1 — Update the System Immediately
Before doing anything else, update your package manager and all installed packages. The VPS image you deployed from may be weeks or months old, containing known vulnerabilities that have already been patched.
sudo apt update && sudo apt upgrade -y
sudo apt autoremove -y
The first command syncs your package list and upgrades all installed packages. The second removes unused dependencies. This step takes only 2–5 minutes but often prevents you from inheriting old, exploitable software.
Step 2 — Create a Non-Root User for Daily Operations
Never use the root account for regular login. Create a dedicated user (e.g., "deployer") with sudo privileges for administrative tasks. This provides a clear audit trail: you can see which user ran which command and when, rather than all activity appearing as "root."
# Create a new user with a home directory
sudo adduser deployer
# Add the user to the sudoers group
sudo usermod -aG sudo deployer
# Switch to the new user and test sudo access
su - deployer
sudo whoami # should return "root"
The adduser command (interactive) is safer than useradd (which requires manual home directory creation). After creating the user, log in as "deployer" and verify that sudo whoami returns "root" — this confirms that sudo is configured correctly.
Step 3 — SSH Key Authentication (The Foundation of Hardening)
Switching from password-based to key-based SSH authentication is the single most important hardening step. Keys use public-key cryptography, making brute-force attacks mathematically infeasible.
Generate a Key Pair on Your Local Machine
Do NOT generate keys on the VPS itself — generate them on your secure local machine (laptop, desktop, etc.). The ed25519 algorithm is modern, fast, and more secure than legacy RSA keys.
# Generate a new Ed25519 key pair
ssh-keygen -t ed25519 -C "[email protected]"
# Press Enter twice to accept the default location (~/.ssh/id_ed25519)
# Optionally, enter a passphrase (recommended for extra security)
Install the Public Key on Your VPS
Your private key stays on your local machine. You copy only the public key to the VPS.
# Method 1: Using ssh-copy-id (easiest)
ssh-copy-id -i ~/.ssh/id_ed25519.pub deployer@YOUR_VPS_IP
# Method 2: Manual copy (if ssh-copy-id is not available)
cat ~/.ssh/id_ed25519.pub | ssh deployer@YOUR_VPS_IP "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
Test Key-Based Login Before Disabling Passwords
This is critical: verify that key login works before disabling password authentication. If you disable passwords without confirming that keys work, you will lock yourself out and need to use the VPS console to recover.
# Test logging in with the key
ssh -i ~/.ssh/id_ed25519 deployer@YOUR_VPS_IP
# You should be logged in without being asked for a password
# If prompted for a passphrase, that's the password protecting your local key (different from server password)
⚠️ Critical Safety Check: Never proceed to the next step (disabling password auth) until you can log in with the key and get a shell prompt. Test from a fresh terminal window to ensure it works. If you disable passwords without confirming key access, you will be locked out.
Step 4 — Harden SSH Configuration
Now that key-based login works, edit the SSH daemon configuration to disable password authentication, root login, and other risky defaults.
sudo nano /etc/ssh/sshd_config
Find or add these lines. In nano, use Ctrl+W to search, then edit:
# Disable root login
PermitRootLogin no
# Disable password authentication (use keys only)
PasswordAuthentication no
PubkeyAuthentication yes
# Limit login attempts and sessions
MaxAuthTries 3
MaxSessions 5
# Timeout for idle connections
ClientAliveInterval 300
ClientAliveCountMax 2
# (Optional) Change the default SSH port from 22 to something else
# Port 2222
# Note: If you change the port, you must update your firewall rules and remember the new port
# Allow only the deployer user to log in
AllowUsers deployer
After editing, save with Ctrl+O, Enter, then Ctrl+X. Now restart the SSH daemon:
sudo systemctl restart sshd
Do NOT log out yet. Open a new terminal and verify that you can still log in. Only after confirming access should you close the original connection.
Step 5 — Configure UFW Firewall
UFW (Uncomplicated Firewall) is a simple front-end to iptables. It allows you to define rules in plain English and automatically handles the complex underlying configuration.
# Install UFW if not already present
sudo apt install ufw -y
# Set default policies: deny all incoming, allow all outgoing
sudo ufw default deny incoming
sudo ufw default allow outgoing
# CRITICAL: Allow SSH BEFORE enabling the firewall, or you'll lock yourself out
sudo ufw allow 22/tcp
# If you changed SSH port: sudo ufw allow 2222/tcp
# Allow HTTP and HTTPS for web servers
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Enable the firewall
sudo ufw enable
# Verify the status
sudo ufw status verbose
The order matters: always add rules before enabling the firewall. After enabling, verify your rules with sudo ufw status verbose and test that you can still SSH in.
UFW App Profiles
UFW includes pre-configured profiles for common services. You can use these instead of specifying ports manually:
# List available profiles
sudo ufw app list
# Enable Nginx (both HTTP and HTTPS)
sudo ufw allow 'Nginx Full'
# Enable OpenSSH (port 22)
sudo ufw allow 'OpenSSH'
Step 6 — Install and Configure Fail2Ban
Fail2Ban monitors log files and automatically bans IP addresses that make repeated failed login attempts. This stops brute-force attacks in their tracks.
sudo apt install fail2ban -y
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
Configure Fail2Ban for SSH
The default configuration usually protects SSH, but create a local config file to customize the behavior:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local
Find the [sshd] section and verify or set these values:
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3 # Ban after 3 failed login attempts
bantime = 3600 # Ban duration: 1 hour (in seconds)
findtime = 600 # Detection window: 10 minutes (in seconds)
Restart Fail2Ban and check the status:
sudo systemctl restart fail2ban
# View currently banned IPs
sudo fail2ban-client status sshd
Step 7 — Enable Automatic Security Updates
Security patches are released constantly. Automatic updates ensure you never miss a critical fix that could be exploited while you're not monitoring the server.
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure --priority=low unattended-upgrades
When prompted, select "Yes" to enable unattended upgrades. This installs a cron job that runs daily and applies all available security updates automatically.
Optional: Configure Automatic Reboot for Kernel Updates
Kernel updates sometimes require a reboot to take effect. You can configure automatic reboot during a maintenance window:
sudo nano /etc/apt/apt.conf.d/50unattended-upgrades
Search for (Ctrl+W) and uncomment these lines:
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "03:00";
Set the time to a low-traffic window for your application (e.g., 3:00 AM). Test this on a non-production server first.
Step 8 — Remove Unnecessary Services
Every running service is a potential attack surface. Disable services you don't need.
# List currently running services
sudo systemctl list-units --type=service --state=running
# Disable unnecessary services (examples)
sudo systemctl disable --now snapd
sudo systemctl disable --now avahi-daemon
sudo systemctl disable --now cups
Also check which ports are open and listening:
sudo ss -tlnp
This shows all listening ports and the process using them. If you see a service you don't recognize or need, disable it.
Step 9 — Kernel-Level Hardening with sysctl
Linux kernel parameters control low-level network behavior. Several can be tuned to improve security against common attack types like IP spoofing, SYN floods, and ICMP redirects.
sudo nano /etc/sysctl.d/99-hardening.conf
Add these lines to create a hardening configuration file:
# Protect against IP spoofing
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Disable ICMP redirects (can be used in some attacks)
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
# Enable SYN cookies to prevent SYN floods
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
# Disable source packet routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
# Disable broadcast ping
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Log suspicious packets
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
Apply the configuration:
sudo sysctl -p /etc/sysctl.d/99-hardening.conf
Step 10 — Audit Your Server with Lynis
Lynis is a free security auditing tool that scans your server for misconfigurations, missing security tools, and hardening opportunities. It then generates a score and detailed recommendations.
sudo apt install lynis -y
sudo lynis audit system
The audit takes 1–2 minutes. At the end, you'll see a "Hardening Index" score from 0–100. Here's how to interpret it:
- 0–25: Minimal security. Dangerous for production.
- 26–50: Basic hardening in place. Still vulnerable.
- 51–75: Good hardening. Suitable for most production servers.
- 76–90: Excellent hardening. Above average security.
- 91–100: Expert-level hardening. Rare and very comprehensive.
For a typical VPS running a web application, aim for ≥65. Lynis will provide specific suggestions (shown as "Suggestion [XXXX]") for improving your score. Many suggestions are optional depending on your use case, but they indicate areas for further hardening.
Additional Security Measures
Enable AppArmor (Ubuntu/Debian)
AppArmor is a Mandatory Access Control (MAC) system that restricts what applications can do. It's often pre-installed but not always enabled:
# Check AppArmor status
sudo aa-status
# If disabled, enable it
sudo systemctl enable apparmor
sudo systemctl start apparmor
Configure Firewall Rules for Services
As you add services (web server, database, etc.), update your firewall rules to allow only necessary ports:
# Example: Allow port 3306 (MySQL) only from internal network
sudo ufw allow from 192.168.1.0/24 to any port 3306
# Example: Allow port 8080 from specific IP
sudo ufw allow from 192.0.2.1 to any port 8080
Monitor Logs Regularly
Log files contain valuable security information. Review them regularly, especially /var/log/auth.log for SSH activity:
# View recent SSH login attempts
sudo tail -f /var/log/auth.log
# Search for failed login attempts
grep "Failed password" /var/log/auth.log | wc -l
Hardening Checklist — Your Roadmap to a Secure Server
| Task | Priority | Time | Impact |
|---|---|---|---|
| Update system packages | Critical | 2 min | Very High |
| Create non-root user | Critical | 3 min | Very High |
| Set up SSH keys | Critical | 5 min | Very High |
| Harden SSH config | Critical | 3 min | Very High |
| Configure UFW firewall | High | 5 min | Very High |
| Install Fail2Ban | High | 3 min | High |
| Enable auto security updates | High | 2 min | Very High |
| Remove unused services | Medium | 3 min | Medium |
| Kernel hardening (sysctl) | Medium | 3 min | Medium |
| Run Lynis audit | Medium | 5 min | Visibility |
After Hardening: What Changes for Your Users?
Server hardening is transparent to legitimate users in most cases. They can still:
- Browse your website normally (HTTP/HTTPS still work)
- Connect to any services you've explicitly allowed through the firewall
- Experience the same application performance (hardening adds minimal overhead)
What changes:
- Bots and automated attacks will see a harder target and move on
- Your server stays patched automatically without manual intervention
- Failed login attempts are logged and repeated offenders are banned
- Administrative access requires key-based authentication, not passwords
Maintaining Your Hardened Server
Hardening is not a one-time task. After the initial setup:
- Monthly: Review Lynis audit recommendations and apply the non-breaking ones
- Weekly: Scan
/var/log/auth.logfor suspicious activity patterns - As needed: Update firewall rules when adding new services
- Automatically: Security patches are applied every day (via unattended-upgrades)
Summary: A hardened server has SSH keys only · Root login disabled · UFW firewall active · Fail2Ban rate-limiting SSH · Automatic security updates enabled · Lynis score ≥65. These six elements place your server well above the baseline security of a typical unmanaged VPS.
Looking for a VPS to Harden?
AsiaGB offers Ubuntu and Debian VPS with full root access, perfect for implementing comprehensive server hardening. Complete control, no restrictions. Starting at 500 THB/month with 99% uptime SLA.
View VPS Plans