Almost as soon as you deploy a server on the internet, it is under attack.

Within seconds, automated bots begin scanning your ports and hammering your SSH login. If you’re using the default settings on your server, then you are more likely to get compromised.

While most cloud providers offer a clean slate, those default configurations are built for convenience, not combat. To truly protect your data, you need to follow industry-standard Linux server security best practices.

Through this guide, you will have a detailed roadmap to secure your VPS with enterprise-grade security. 

Why a Fresh Linux VPS Is a Target for Hackers

As soon as your cloud provider assigns a public IPv4 address to your server, the clock starts. Security researchers and malicious botnets continuously scan the entire IPv4 address space using tools such as Shodan, Censys, and Zmap.

Honeypot data consistently shows that a new, exposed Linux server will experience its first automated SSH login attempt within 3 to 5 minutes of going live.

If you leave default settings intact, it isn’t a matter of if you get breached, but when. If you don’t protect your server, an automated script will root your server, deploy a crypto-mining payload, and potentially leave you with a thousand-dollar cloud compute bill overnight.

What Does the “Attack Surface” Mean?

The “attack surface” is the exact combination of open ports, default configurations, and predictable patterns your server exposes to the internet. A fresh VPS usually has:

  • Port 22 open to the world: The universal beacon for SSH brute-force scripts.
  • Root login enabled: Giving attackers the ultimate username; they only need to guess the password.
  • Password authentication is enabled, allowing unlimited dictionary attacks against your login prompt.

If you provision your servers through a control panel like RunCloud, much of this attack surface is already minimized for you. But if you are managing a bare-metal VPS yourself, run the commands below to manually lock it down.

However, any one single measure won’t be enough to protect your server; that’s why we recommend following the “Swiss Cheese Model of Security”.

Suggested read: 10 Security Tips to Secure VPS Server in 2025 [Ultimate Guide] 

The Swiss Cheese Model of Security

This model is built on the principle that security should never rely on a single control, as even the best defense has holes, or “slices” of weakness. 

In this model, each layer of security (like disabling root login, configuring UFW, enabling Fail2Ban, etc.) is represented by a slice of Swiss cheese. Each slice has holes representing vulnerabilities, misconfigurations, or human error.

  • A single slice (one defense) is easily penetrated if an attacker’s exploit aligns with the hole in that single layer.
  • Multiple slices stacked together provide defense-in-depth. While the holes in the first slice (e.g., a custom SSH port) might align with the threat, the second slice (e.g., SSH key authentication) or the third slice (e.g., Fail2Ban) is highly unlikely to have a hole in the exact same spot.
The Swiss Cheese Model of Security for Linux Server Hardening

By stacking all 11 steps in this guide, we can ensure that even if one defense fails, the next layer (or the layer after that) will stop the threat, preventing it from reaching your core application.

Suggested read: 5 Ways to Fix the SSH Connection Refused Error [SOLVED] 

How to Harden a Linux Server

Follow the steps below to protect your Linux server on the internet:

Step 1: Disable Root Login and Create a Sudo User

Performing regular maintenance activities on your server as the root user is dangerous – a single typo can destroy your system.

To protect your system, we recommend creating an unprivileged user and granting it administrative rights via sudo.

Connect to your VPS as root, then run:

# Replace 'sysadmin' with your preferred username
adduser sysadmin

You will be prompted to set a password. Make it strong, even though we will disable password logins shortly. Skip the contact information prompts by hitting Enter.

Next, add your new user to the sudo group so you can execute administrative commands:

usermod -aG sudo sysadmin

Verify it works before logging out. Switch to your new user and test sudo:

su - sysadmin
sudo ls -la /root

If you are prompted for your password and can successfully see the contents of the root directory, your sudo user is ready.

With RunCloud, you can manage users and permissions for your Linux server directly from the web dashboard, without SSHing into the server. 

Step 2: Switch to SSH Key Authentication and Disable Password Login

A secure password is hard to remember, and a weak password can be cracked immediately. That’s why all cybersecurity experts agree that cryptographic keys are a better replacement for your username/password based logins.

In this step, we are going to replace password authentication with an ed25519 SSH key pair (which is faster and more secure than older RSA keys).

Generate your key pair locally

Do not run this on your VPS. Open a new terminal on your local computer (your Mac, Windows, or local Linux machine):

ssh-keygen -t ed25519 -C "[email protected]"

Hit Enter to save the key to the default location (~/.ssh/id_ed25519). When prompted, you can set a strong passphrase to encrypt the key on your local disk or leave it blank if you don’t want to encrypt it.

Copy the public key and lock down the sshd_config

Still on your local computer, copy the public key to your VPS, targeting your new sudo user:

ssh-copy-id sysadmin@YOUR_VPS_IP

Now, go back to the terminal window connected to your VPS. It’s time to edit the SSH daemon configuration to disable password logins and root access permanently.

sudo nano /etc/ssh/sshd_config

Find the following lines, uncomment them (remove the #), and change their values to match these exactly:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes

Save and exit (CTRL+O, Enter, CTRL+X). Do not restart the SSH service just yet; we are going to change the port in the next step.

Note: RunCloud users can add SSH keys to their servers simply by pasting their public key into the RunCloud dashboard, no nano or config editing required.

Step 3: Change the Default SSH Port

Most automated scripts scan for and try to exploit port 22. Moving SSH to a non-standard high port (between 1024 and 65535) won’t stop a targeted attack, but it drops botnet noise by 99%, keeping your auth logs clean and saving CPU cycles.

Open the SSH config file again:

sudo nano /etc/ssh/sshd_config

Find the line that says #Port 22. Uncomment it and change it to your desired port. For this example, we will use 52222:

Port 52222

Save and exit.

Warning: DO NOT restart SSH until we configure the firewall in Step 4, or you will permanently lock yourself out.

Step 4: Configure UFW to Allow Only What You Need

Ubuntu and Debian servers use UFW (Uncomplicated Firewall) to manage network connections. To protect your server, we recommend setting a default-deny policy for incoming traffic, allowing outgoing traffic, and explicitly opening only the ports we need.

Run the following commands on your VPS:

# Deny all incoming traffic by default
sudo ufw default deny incoming


# Allow all outgoing traffic by default
sudo ufw default allow outgoing


# Allow your NEW custom SSH port (crucial!)
sudo ufw allow 52222/tcp


# Allow HTTP and HTTPS if you are hosting web apps
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

Review your staged rules:

sudo ufw show added

If everything looks correct, you can enable the firewall by running the following command:

sudo ufw enable

Once the firewall is enabled, any new traffic entering or leaving your server will be inspected and filtered according to the rules we configured above. If any application has already established a connection, it won’t be terminated, but if the application attempts to establish a new connection, it will be blocked by the firewall. 

Now that the firewall allows traffic on your custom port, we can safely apply the SSH changes by running the following command:

sudo systemctl restart ssh

Testing phase: DO NOT CLOSE your current terminal session. Open a new terminal on your local machine and test your new setup:

ssh -p 52222 sysadmin@YOUR_VPS_IP

If you successfully connect using your SSH key, you can close the original root session.

Note: If configuring firewalls over CLI makes you nervous, RunCloud’s Firewall Manager lets you set, preview, and deploy port rules and IP whitelists directly from the dashboard without touching the terminal.

Suggested read: Enable Zero-Trust SSH with Cloudflare on Windows, Mac, Linux, and ChromeOS 

Step 5: Install Fail2Ban to Block Brute-Force Attacks

Now that we have changed the SSH port and disabled password authentication, the server is relatively secure, but automated bots will still try to break in by sending random login attempts with incorrect credentials.

Fail2Ban monitors your log files and dynamically updates your firewall to block IP addresses that show malicious behavior.

To configure this on your Linux server, you can install the Fail2Ban package using the following command:

sudo apt update && sudo apt install fail2ban -y

After installing it, you need to create a set of rules (called “jails”) for your server. We strongly recommend that you don’t edit the default jail.conf file, as package updates will overwrite it. Instead, you should copy it to create a new file called jail.local. You can do this on a Linux server using the following command:

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

After creating the file, you can edit your local configuration:

sudo nano /etc/fail2ban/jail.local

Scroll down to the [sshd] block. You need to tell Fail2Ban that you are using a custom port, and explicitly enable the jail. Modify the block to look like this:

[sshd]
enabled = true
port    = 52222
logpath = %(sshd_log)s
backend = %(sshd_backend)s
maxretry = 3
bantime = 1h

Run the following commands to save and exit, then start and enable the service:

sudo systemctl enable fail2ban
sudo systemctl restart fail2ban

After creating the service, you can verify that your SSH jail is active using the following command:

sudo fail2ban-client status sshd

In the screenshot above, we can see the list of IP addresses that Fail2Ban has banned from accessing our server.

By completing these 5 steps, you have eliminated the low-hanging fruit that compromises 95% of fresh Linux setups. 

Note: Getting Fail2Ban thresholds wrong in jail.local often results in banning yourself or failing to trigger on real attacks. That’s why RunCloud ships with Fail2Ban pre-configured for web and SSH traffic. 

Suggested read: How to Use Cloudflare Firewall Rules to Protect Your Web Application 

Step 6: Enable Automatic Security Updates

A hardened server is only secure until the next CVE is published. If you are managing more than one server, you should not want to manually run apt upgrade whenever a vulnerability is discovered in OpenSSL or your kernel. Enable unattended-upgrades to automatically install critical security patches in the background.

To do this, first you need to install the necessary packages using the following command:

sudo apt update && sudo apt install unattended-upgrades apt-listchanges -y

After installing the services, you can enable the service via the interactive prompt:

sudo dpkg-reconfigure -plow unattended-upgrades

Select Yes when prompted to automatically download and install stable updates.

After configuring it, check the configuration file to verify that it has been activated successfully using the following command:

cat /etc/apt/apt.conf.d/20auto-upgrades

When you run the above command, you should see APT::Periodic::Unattended-Upgrade "1"; in the output.

Step 7: Remove Unused Packages and Disable Unnecessary Services

Every service running on your server is a potential entry point for hackers. If you aren’t using a service or application, you can turn it off to protect your server and conserve resources.

To do this, first, we will audit what is actively listening on your server’s network interfaces by using the following command:

sudo ss -tulpn

If you see any services that you don’t want, then you can stop and disable them so they don’t start on reboot:

sudo systemctl stop <name>
sudo systemctl disable <name>

In the above commands, replace the <name> with the actual name of the service that you want to disable. 

Next, purge any orphaned packages and dependencies that came pre-installed on your provider’s OS image but which aren’t needed anymore:

sudo apt autoremove --purge -y

Step 8: Harden Kernel Parameters with sysctl

By default, the Linux kernel uses networking parameters optimized for broad compatibility rather than strict security. When you deploy your server on the internet, it will be constantly bombarded with hundreds of attacks that try to exploit these compatibility features. 

But you can mitigate several types of network attacks (like SYN floods and IP spoofing) by tweaking sysctl.conf. To do this, you can open the configuration file using the following command:

sudo nano /etc/sysctl.conf

In this file, we will disable certain features by appending the following lines to the bottom of the file:

# Protect against SYN flood attacks
net.ipv4.tcp_syncookies = 1


# Ignore ICMP broadcast requests (prevent smurf attacks)
net.ipv4.icmp_echo_ignore_broadcasts = 1


# Disable ICMP redirects (prevent man-in-the-middle routing attacks)
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0


# Log spoofed packets, source routed packets, and redirect packets
net.ipv4.conf.all.log_martians = 1

After editing the file, you can save and exit the file editor (CTRL+O, Enter, CTRL+X). After that, you can apply the changes immediately without rebooting by running the following command:

sudo sysctl -p

Suggested read: 16 Best Linux Distros in 2025 

Step 9: Set Strict File Permissions and Audit User Accounts

If an attacker compromises a system, they will try to either create hidden backdoor users, or leave files with wide-open permissions. There are several steps you can take to ensure this isn’t the case on your server. First, you can audit your user accounts to ensure only root has a User ID (UID) of 0. Run this command to print any user with root-level privileges:

awk -F: '($3 == "0") {print}' /etc/passwd

This should output exactly one line: root:x:0:0:root:/root:/bin/bash. If you see any other user here, then it is possible that your server is compromised.

Next, verify that no users have empty passwords:

sudo awk -F: '($2 == "") {print}' /etc/shadow

This should return no output.

Finally, find and review any world-writable directories (directories anyone can write to) that don’t have the “sticky bit” set (which prevents users from deleting each other’s files):

sudo find / -type d -perm -0002 -a ! -perm -1000 -print 2>/dev/null

If your server is serving multiple websites, then the above command will probably return a long list of directories. You need to review this list and, if you find any rogue directories, investigate them immediately and restrict their permissions using chmod 755.

Step 10: Review Mandatory Access Control (AppArmor and SELinux)

AppArmor (on Ubuntu/Debian) and SELinux (on RHEL/AlmaLinux) are Mandatory Access Control (MAC) systems. They act as a high-level security guard built directly into the Linux kernel. While standard file permissions (chmod) control who can see a file, MAC systems control which specific programs are allowed to do what.

In a standard setup, if a hacker exploits a vulnerability in a web server such as NGINX and gains “root” access, they can theoretically access every file on your server.

With AppArmor or SELinux active, the program is confined to a “sandbox.” Even if NGINX is compromised, the MAC system detects that NGINX is attempting to access sensitive system files (such as/etc/shadow) or execute unauthorized commands. Because that behavior isn’t in the program’s predefined “security profile,” the kernel blocks the action instantly, even if the attacker has root privileges. It effectively limits the “blast radius” of any potential hack.

You can run the following commands to check the configuration of these systems on your server:

  • On Ubuntu/Debian (AppArmor):
sudo aa-status
  • On RHEL/Alma/Rocky:
sestatus

Manually configuring MAC systems is tricky, and beyond the scope of this article. It requires writing deep-level security profiles that define every single file, port, and network socket a program is allowed to touch. One small mistake in a profile can cause your database to crash or prevent your website from loading, leading to hours of frustrating troubleshooting.

The good news is that if you are using RunCloud, you don’t need to lift a finger.

RunCloud servers are engineered to be secure out of the box. The platform automatically configures and optimizes these security layers during server provisioning. Your server is hardened the moment it connects to the RunCloud panel, allowing you to focus on your applications while RunCloud handles the complex kernel security in the background.

Step 11: Configure Off-Server Backups

Hardening your server reduces the risk of a hack, but it cannot protect you against hardware failure, a data center fire, or an accidental rm -rf / command. Off-server backups ensure that even if your entire VPS is deleted, your business can be restored in minutes.

There are several ways to handle backups, each with its own pros and cons:

  1. Disk-Level Snapshots: Taking a full image of your server via your provider (like DigitalOcean or AWS). These are easy but often expensive, and they’re hard to move between providers.
  2. Application Plugins: Using WordPress plugins like UpdraftPlus. These are user-friendly, but they can slow down your site because they use your server’s PHP resources to compress files.
  3. Manual Scripting: Using Linux tools to manually move data. If you choose to do this manually, you must manage three distinct parts: the database, the files, and the transport. 
    • Security: Manual rclone or script configs often store your Cloud API keys or Database passwords in plaintext on the server. If a hacker gets in, they now have your backup keys too.
    • Resource-Heavy: Compressing large folders (tar) and dumping databases every night causes high CPU and Disk I/O spikes, which can make your website sluggish during the backup window.
    • Reliability: If the script fails, you won’t know until you try to restore and find out that the files are empty.

If you are using RunCloud, you don’t need to deal with any of this.

RunCloud uses Incremental Backups, which is a far superior technology. Instead of zipping your entire site every night (which is slow and uses a lot of disk space), RunCloud only identifies the specific data that changed – and syncs just that.

  • Fast & Efficient: Because it only moves “changes,” backups finish in seconds rather than minutes.
  • Zero Resource Lag: It doesn’t put a heavy load on your server, keeping your website fast even during a backup.
  • Encrypted & Secure: Your S3 or Backblaze credentials are stored in RunCloud’s encrypted vault.
  • Backup Notifications: You can configure the Backup script to notify you via Slack/Email/Discord if the backup fails for any reason.
  • One-Click Restore: If something goes wrong, you don’t have to remember complex Linux commands. You just click “Restore” in the dashboard, and RunCloud puts everything back exactly where it belongs.
Runcloud automated backups

After Action Report

If you have followed all the steps in this article, your server is now locked down and can withstand a variety of internet attacks. But a hardened server isn’t very useful if it doesn’t host anything. The next step is installing your web stack (NGINX/Apache, PHP, MySQL) and provisioning SSL certificates.

Doing this manually means diving right back into the terminal. After hardening, managing NGINX, PHP-FPM, and SSL still requires SSH for every single configuration change, virtual host creation, and certificate renewal.

RunCloud manages your NGINX configuration, PHP-FPM tuning, and Let’s Encrypt SSL deployments entirely from a UI, while fully respecting the hardened SSH and firewall configurations you just put in place. 

While RunCloud simplifies complex server management tasks, it is designed for developers, agencies, and power users who need more than just a basic cPanel replacement. Once your servers are hardened, RunCloud enables you to scale your operations by offering tools for advanced management:

  • Multi-Server Management: Easily oversee, update, and manage dozens or hundreds of hardened Linux servers from a single dashboard.
  • Team & Role-Based Permissions: Delegate server access to team members or clients without sharing SSH keys or root passwords, thanks to granular control over who can manage applications, databases, or backups.
  • API-Driven Control: Integrate server and application management into your custom workflows using the RunCloud API, allowing for automated server provisioning and deployment.

Start using RunCloud today.

Frequently Asked Questions

What is Linux server hardening?

Linux server hardening is the process of reducing a system’s attack surface by patching vulnerabilities, disabling unused services, and implementing strict access controls. Common hardening steps include disabling root SSH access, configuring firewalls like UFW, and enforcing cryptographic key-based authentication.

How long does it take to harden a Linux server?

Manually executing a basic Linux hardening checklist on a fresh VPS typically takes an experienced sysadmin about 30 minutes. However, advanced hardening procedures like configuring SELinux, setting up intrusion detection systems, and passing compliance audits can take several hours to properly tune. 

Should I run hardening on an existing server or only on fresh ones?

You should ideally harden a fresh Linux server before it is ever exposed to public internet traffic or connected to your production application stack. Applying strict firewall rules, altering permissions, and modifying SSH configurations on an existing server carries a high risk of breaking active application dependencies or accidentally locking yourself out. If you must harden an existing production server, thoroughly test the new security policies in a staging environment and ensure you have recent, verified off-site backups first.

Does changing the SSH port actually improve security?

Changing the default SSH port from 22 to a non-standard high port is a security-through-obscurity tactic that will not stop a determined, targeted attacker running a full port scan. However, it is still highly recommended because it drops automated botnet brute-force attempts by over 99 percent. This drastically cleans up your system authentication logs, reduces wasted CPU cycles, and prevents tools like Fail2Ban from being overwhelmed by background internet noise.