Complete Guide to Linux Server (VPS) Hardening from Start to Finish

📅 August 1, 2026 ⏱ Reading time: 7 minutes

Server security is one of the most critical aspects of digital infrastructure management. The SSH (Secure Shell) protocol, as the primary gateway for server management, is constantly targeted by cyber attacks. In this article, you will learn about SSH hardening and receive a practical checklist for securing your server.

Linux Server Hardening Guide
How to make your Linux server more secure

🔐 Complete Guide to Linux Server (VPS) Hardening from Start to Finish

Server security is one of the most critical aspects of digital infrastructure management. The SSH (Secure Shell) protocol, as the primary gateway for server management, is constantly targeted by cyber attacks. In this article, you will learn about SSH hardening and receive a practical checklist for securing your server.

📌 What Is SSH Hardening?

SSH hardening refers to a set of settings and changes in the configuration file /etc/ssh/sshd_config aimed at reducing the attack surface and closing security gaps. This includes disabling weak authentication methods, restricting access, and using strong encryption.

🛡️ Golden Checklist for SSH Hardening

Setting Ideal State Description
PermitRootLogin ❌ no Disables direct login for the root user. This is the most important security step.
PasswordAuthentication ❌ no Disables password-based login. Makes brute-force attacks ineffective.
PubkeyAuthentication ✅ yes Enables public key authentication. The most secure method for server access.
KbdInteractiveAuthentication ❌ no Disables interactive authentication (typically used for passwords).
Non-root user with sudo ✅ Enabled Create a regular user (e.g., admin) and grant sudo access.

⚙️ Implementation Steps for Hardening

  1. Create a new user: Create a non-root user with sudo access.
    sudo adduser admin
    sudo usermod -aG sudo admin
  2. Set up SSH keys: Generate an SSH key for the new user and place the public key in ~/.ssh/authorized_keys.
    # On your local system
    ssh-keygen -t ed25519 -a 100
    ssh-copy-id admin@server_ip
  3. Edit sshd_config: Open the SSH configuration file with a text editor.
    sudo nano /etc/ssh/sshd_config
  4. Apply settings: Set the following values in the file:
    PermitRootLogin no
    PasswordAuthentication no
    PubkeyAuthentication yes
    KbdInteractiveAuthentication no
    ChallengeResponseAuthentication no
    UsePAM no
  5. Reload SSH: After saving changes, reload the SSH service.
    sudo systemctl reload sshd
  6. Test connection: Before closing the current session, open a new terminal and connect with the new user and SSH key to make sure everything works correctly.
    ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no admin@server_ip
    # You should get "Permission denied (publickey)"

⚠️ Critical Note

Never close the current SSH session before testing the new connection! If an error occurs, you may lose access to your server. Always keep an active connection for troubleshooting.

🔥 Next Step: Firewall Configuration (UFW)

A firewall acts like a doorman that only allows authorized services to enter.

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status numbered

Expected output:

[ 1] 22/tcp                     ALLOW IN    Anywhere
[ 2] 80/tcp                     ALLOW IN    Anywhere
[ 3] 443/tcp                    ALLOW IN    Anywhere

🛡️ HTTPS with Let's Encrypt

  1. Install Certbot:
    sudo apt update
    sudo apt install certbot python3-certbot-apache  # For Apache
    # Or
    sudo apt install certbot python3-certbot-nginx   # For Nginx
  2. Obtain certificate:
    sudo certbot --apache -d yourdomain.com -d www.yourdomain.com
  3. Verify auto-renewal:
    sudo certbot renew --dry-run
  4. Check TLS settings:
    # In the web server configuration file
    SSLProtocol -all +TLSv1.2 +TLSv1.3
    SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256

✅ Security Tip

After enabling HTTPS, be sure to add the X-Content-Type-Options: nosniff header to the web server to prevent MIME-type sniffing attacks.

🗑️ Managing Unnecessary Services

Reviewing and disabling services that don't need to run reduces the attack surface.

Services that can be disabled:

sudo systemctl disable --now ModemManager
sudo systemctl disable --now multipathd
sudo systemctl disable --now multipathd.socket
sudo systemctl disable --now udisks2
sudo systemctl disable --now upower

Before disabling any service, check its dependencies to make sure other services won't be affected. For example, for ModemManager:

systemctl status ModemManager.service
# From here you can see:
# Whether it's active/disabled
# When it ran
# Latest logs
# PID
# Sometimes why it's active
# For more detailed check:
# systemctl cat udisks2.service


# Check the service's own dependencies
systemctl list-dependencies ModemManager.service
# This shows what ModemManager depends on to run.

systemctl list-dependencies --reverse ModemManager.service
# If I turn off this service, which units depend on it?


# For services that likely have specific purposes
# For example, before disabling multipathd
sudo multipath -ll
# And for ModemManager
sudo mmcli -L


📌 Important Note

list-dependencies alone does not prove that disabling a service is safe. We must examine both the dependency graph and the actual role of the service on the server.

For this reason, for example, qemu-guest-agent was not disabled even though it's a small service; because we verified the VPS runs on KVM and actually uses it.

Some services like polkit and udisks2 are part of the system base layer and other services like hypervisor and KVM may use them. Disabling them solely to reduce service count is not worth the risk.

🌐 DNS Hardening

The DNS server is one of the critical services that must be separately secured.

Security Measure Description
Disable Recursion On authoritative servers, disable the recursive feature to prevent them from being used for amplification attacks.
Rate Limiting To prevent DDoS attacks and reduce server load, configure the maximum number of allowed responses per second.
Zone Transfer Management Restrict zone transfers to specific IPs only and use TSIG signing for authentication.
Enable DNSSEC For authenticating and verifying DNS response integrity and preventing cache poisoning attacks.
Encrypted Traffic (DoT/DoH) For protecting user privacy and encrypting traffic between client and resolver.

Verify DNS from Outside

To ensure DNS is properly configured, test from outside:

dig @your_server_ip yourdomain.com
nslookup yourdomain.com your_server_ip

🔍 External Verification

Before declaring the work complete, be sure to test the services from the perspective of an internet user:

1. Test SSH from outside

# From your own system (not on the server)
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no admin@your_server_ip
# You should get "Permission denied"

2. Test HTTP and HTTPS

curl -I http://yourdomain.com
# Should return 301 Moved Permanently → HTTPS

curl -I https://yourdomain.com
# Should return 200 OK

3. Check security headers

curl -I https://yourdomain.com | grep -E "X-Content-Type-Options|Strict-Transport-Security"

✅ Final Confirmation

After completing all steps, verify the following status:

  • ✅ SSH key login for admin user works.
  • ✅ admin user has root access via sudo.
  • ✅ Direct root login is disabled.
  • ✅ Password authentication for SSH is disabled; only SSH keys are accepted.
  • ✅ Firewall is active and only ports 22, 80, and 443 are open.
  • ✅ HTTP redirects to HTTPS.
  • ✅ SSL certificate is valid and auto-renewal is enabled.
  • ✅ Unnecessary services are disabled.
  • ✅ Essential services (polkit, qemu-guest-agent) are preserved.
  • ✅ DNS is properly configured and accessible from outside.
  • ✅ X-Content-Type-Options: nosniff header is active.

🎯 By completing these steps, you increase your server's security by up to 90% against common attacks.

🔧 The Remaining 10%: Advanced Measures

For maximum security, consider these additional measures:

1. Change the Default SSH Port

# In /etc/ssh/sshd_config
Port 2222  # Or any number above 1024

Why? Automated attacks typically only scan port 22.

2. Restrict Allowed IPs (Firewall)

# With UFW
sudo ufw allow from 192.168.1.0/24 to any port 22

# With iptables
iptables -A INPUT -p tcp --dport 22 -s 192.168.1.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP

Why? Only specific IPs (e.g., internal network or office) are allowed to connect.

3. Use Fail2ban

sudo apt install fail2ban -y
sudo systemctl enable fail2ban

Why? After several failed login attempts, it blocks the attacker's IP for a specified duration.

4. Fully Disable SSH for root User

# In /etc/ssh/sshd_config
PermitRootLogin prohibit-password  # Don't allow login even with key

5. Set Timeout for Idle Sessions

# In /etc/ssh/sshd_config
ClientAliveInterval 300
ClientAliveCountMax 2

Why? Disconnects idle sessions after 10 minutes.

6. Use SSH Keys with Stronger Algorithms

# Use ED25519 instead of RSA
ssh-keygen -t ed25519 -a 100 -C "your_email@example.com"

Why? ED25519 is more secure and faster than RSA.

7. Enable Logging and Monitoring

# In /etc/ssh/sshd_config
SyslogFacility AUTH
LogLevel VERBOSE

# Check logs
sudo tail -f /var/log/auth.log | grep sshd

8. Use Two-Factor Authentication (2FA)

# Install Google Authenticator
sudo apt install libpam-google-authenticator
google-authenticator

Then in /etc/ssh/sshd_config:

ChallengeResponseAuthentication yes
AuthenticationMethods publickey,password publickey,keyboard-interactive

9. Regularly Update OpenSSH

sudo apt update && sudo apt upgrade openssh-server

Why? Security bugs discovered in newer versions are fixed.

🔍 Security Verification Tools

Check current settings

sudo sshd -T | grep -E "permitrootlogin|passwordauthentication|pubkeyauthentication"

Check login logs

sudo grep "Failed password" /var/log/auth.log | tail -20

View active connections

sudo ss -tulpn | grep -E ":(22|80|443)"

📊 Summary of the Remaining 10%

Solution Difficulty Level Security Impact
Change SSH port Easy ⭐⭐
Restrict IPs Medium ⭐⭐⭐⭐
Fail2ban Medium ⭐⭐⭐⭐
ED25519 keys Easy ⭐⭐⭐⭐
2FA Hard ⭐⭐⭐⭐⭐
Regular updates Easy ⭐⭐⭐⭐⭐

📌 Final Recommendation

To achieve 100% relative security, add at least these three items to the previous 90%:

  1. Change SSH port (simplest and most effective)
  2. Install Fail2ban (automatic protection against attacks)
  3. Restrict allowed IPs (if possible)

With these steps, your server security reaches a very high level and common attacks become practically ineffective.

🏁 Final Summary

SSH hardening is one of the most fundamental and effective security measures for any Linux server. By implementing these steps:

  • ✅ You increase your server's security by up to 90% against common attacks
  • ✅ You prevent unauthorized access
  • ✅ You make login logs more transparent and traceable

Next step: After completing this work, you can move on to more advanced topics like setting up Fail2ban, changing the SSH port, and using 2FA to bring your server security to an even higher level.

📌 Final Reminder

Security is a continuous process, not a one-time task. Always check your logs and keep the system updated.