When I set up a new VPS, there are fundamental security steps I take within the first 45 minutes, before exposing the server to the outside world. The hardening procedures I perform during this time are critical for fending off most simple attack attempts and establishing a more secure operating environment. My goal is to get the server ready for use immediately while also building a minimum defense against known vulnerabilities.
This checklist applies to any Linux-based VPS, whether it’s for a web application, a database server, or a backend for my own automation platform. Skipping basic security steps can lead to much larger problems down the line. Therefore, being proactive from the moment of initial setup is always the best approach.
SSH Access and Authentication: Bolstering the Gates
Initial access to a VPS is typically via SSH, and this is one of the most common services targeted by attackers. The default SSH port (22), permission to log in with the root user, and password-based authentication leave your server vulnerable to brute-force attacks. That’s why my first task is to tighten SSH access as much as possible.
First, I move the SSH port from the standard 22 to a different, high-numbered port. This eliminates a large portion of automated bots and simple scans, reducing noise. Then, I prohibit direct login for the root user via SSH and enforce SSH key-based authentication only. Completely disabling password login provides significant protection against potential password leaks or weak password guesses.
# First, choose a new port (e.g., 2222)
# Create a new admin user and add your SSH key
# (These steps will be explained in more detail below)
# Edit the SSH configuration file
sudo nano /etc/ssh/sshd_config
In the sshd_config file, I make the following changes:
# Change Port 22 to a different port (e.g., 2222)
Port 2222
# Prohibit direct login for the root user
PermitRootLogin no
# Disable password authentication (use SSH key only)
PasswordAuthentication no
# Prohibit logins with empty passwords
PermitEmptyPasswords no
# Allow only specific users/groups with AllowUsers or AllowGroups
# For example: AllowUsers mehmet
After making these changes, I restart the SSH service. However, make sure you test access with the new port and user before performing this step. Otherwise, you might lose access to your server.
sudo systemctl restart sshd
Firewall Configuration: Opening Only What’s Needed
One of the most fundamental steps in securing a server is configuring a firewall. Without a firewall, all ports on your server become accessible from the outside. This invites all kinds of attack attempts. My first task is to open only the ports I truly need using an easy-to-manage tool like UFW (Uncomplicated Firewall) or firewalld.
In most cases, on a new VPS, I only need SSH (the port we changed above), HTTP (80), and HTTPS (443) ports. Keeping all other ports closed by default significantly reduces the attack surface. For example, if I’m setting up a database server and this database will only be accessed from within the same VPC, I open the database port (like 5432 for PostgreSQL) only to the internal network, never to the outside.
# UFW installation and basic settings on Ubuntu-based systems
sudo apt update
sudo apt install ufw -y
# By default, deny all incoming connections, allow outgoing
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow our SSH port (e.g., 2222)
sudo ufw allow 2222/tcp
# Allow HTTP and HTTPS ports
sudo ufw allow http
sudo ufw allow https
# Enable UFW
sudo ufw enable
After applying these steps, I check that the rules are correctly applied with the sudo ufw status verbose command. For CentOS or Red Hat-based systems using firewalld, I use firewall-cmd commands with a similar logic. For example, specific ports are allowed with commands like firewall-cmd --add-port=2222/tcp --permanent. This approach prevents unnecessary services or accidentally open ports from being exploited.
System Updates and Basic Package Management
When I set up a new server, one of my first tasks is to update the operating system and all installed packages to their latest versions. Many security vulnerabilities stem from known exploits in older versions of software. These vulnerabilities are usually patched, but your server remains vulnerable until you apply these patches. This situation, especially in MSP operations, requires the discipline of keeping hundreds of servers up-to-date instantly.
The update process not only refreshes existing packages but also improves system performance and stability. Cleaning up unused packages or those with removed dependencies after an update is also a good habit. This saves disk space and eliminates unnecessary potential sources of vulnerabilities.
# For Ubuntu/Debian based systems:
sudo apt update # Updates package lists
sudo apt upgrade -y # Upgrades installed packages
sudo apt dist-upgrade -y # Upgrades the kernel and system distribution
sudo apt autoremove -y # Removes unused dependencies and packages
sudo apt clean # Cleans downloaded package files
# For CentOS/Red Hat based systems:
sudo dnf update -y # Updates all packages
sudo dnf autoremove -y # Removes unused dependencies
Immediately after these steps, I consider configuring automatic updates. Especially for critical security patches, automatic application is a great convenience for most small and medium-sized businesses (SMBs). On Ubuntu, the unattended-upgrades package is quite useful for this task. This approach simplifies my processes for managing patching windows and alert triage.
# Configure automatic updates (Ubuntu example)
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure --priority=low unattended-upgrades
User Management and Privilege Escalation Control
When I set up a new VPS, most providers give me direct access with the root user. However, direct and continuous use of the root user is a major security risk. The root user has unlimited privileges over the entire system, and if these privileges are misused or compromised, the entire server is jeopardized. Therefore, one of the critical steps I take within the first 45 minutes is to correctly configure privileged user management.
I create a new administrative user, grant this user sudo privileges, and restrict direct use of the root user. This is in line with the “least privilege” principle. That is, I use only the minimum privileges required to perform a task. When I run a command using sudo, this operation is recorded in the sudo logs, which is important for auditability.
# Create a new user (e.g., 'mehmet')
sudo adduser mehmet
# Grant sudo privileges to the new user (for Ubuntu/Debian, add to the 'sudo' group)
sudo usermod -aG sudo mehmet
# For CentOS/Red Hat, add to the 'wheel' group
# sudo usermod -aG wheel mehmet
After creating the new user and granting sudo privileges, I have already disabled password login for the root user in the SSH configuration above. If I’m logging in with an SSH key, I also prefer to disable SSH key login for the root user. This ensures that the root account can only be used via console access or the sudo command. This way, I can prevent issues like privilege creep.
Monitoring and Logging Fundamentals: Knowing What Happened
The first steps to securing a server are to make it observable. When a problem occurs or an attack attempt is made, I need logs and system metrics to know what happened and react quickly. When setting up a new VPS, within the first 45 minutes, I configure basic logging and fail2ban, a simple intrusion prevention tool. These steps are like opening the server’s “eyes and ears.”
On Linux systems, journald, which comes with systemd, centrally collects and manages system logs. By examining these logs, I can see system anomalies or error messages. SSH access logs, in particular, show who tried to log in from which IP and when, allowing me to detect potential brute-force attacks.
# To see the last 100 SSH attempts (Ubuntu/Debian)
journalctl -u sshd -n 100
# To see logs from a specific time period
journalctl --since "2 hours ago" --unit=sshd
fail2ban, on the other hand, monitors log files and, when it detects specific patterns (e.g., numerous failed SSH login attempts), it temporarily blocks the IP addresses of the attackers in the firewall. This is a simple but effective brute-force defense mechanism and reduces bot traffic that unnecessarily occupies the server.
# fail2ban installation and basic configuration
sudo apt install fail2ban -y
# Copy the default configuration file
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
# Edit the jail.local file to adjust it to your SSH port
# nano /etc/fail2ban/jail.local
# In the [sshd] section, set the 'port' value to your custom SSH port (e.g., 2222).
# enabled = true
# Restart the fail2ban service
sudo systemctl restart fail2ban
These basic monitoring and blocking mechanisms form the server’s first line of defense. In later stages, I can set up a more comprehensive observability platform with tools like InfluxDB, Grafana, and Loki. However, starting with journald and fail2ban in the first 45 minutes saves me from many problems. Alert triage and noise reduction are topics I constantly work on in MSP operations, so getting these steps right from the start is crucial.
Additional Security Layers and Advanced Considerations
The basic security steps I apply in the first 45 minutes protect the server against many common threats. However, a full-fledged security posture requires more. These additional layers are usually shaped by the specific requirements of the application or service and are measures that should be implemented shortly after initial setup, if not immediately. In this section, I address some advanced security considerations that go beyond the first 45 minutes but should be kept in mind.
For example, Mandatory Access Control (MAC) systems like SELinux (Security-Enhanced Linux) or AppArmor provide an extra layer of security by restricting access to system resources for specific applications or users. These tools require experience, as they can cause system disruptions if misconfigured. If I’m setting up a Red Hat or CentOS system, I enable SELinux; on Ubuntu, I prefer AppArmor.
# Check SELinux status (CentOS/Red Hat)
sestatus
# Set SELinux to enforcing mode (if it's in permissive mode)
# sudo setenforce 1
Against memory vulnerabilities, some kernel-level protections can be enabled with sysctl settings. For example, Address Space Layout Randomization (ASLR) reduces the effectiveness of attacks like buffer overflows.
# sysctl setting to enable ASLR
sudo sysctl -w kernel.randomize_va_space=2
# To make it persistent, add it to /etc/sysctl.conf
# echo "kernel.randomize_va_space=2" | sudo tee -a /etc/sysctl.conf
If I’m running a reverse proxy or web server like Nginx on the server, I also configure its basic security settings. Steps like rate limiting, HTTP header hardening (X-Frame-Options, X-Content-Type-Options, Content-Security-Policy), and disabling unnecessary modules reduce the attack surface at the web application layer. If I’m deploying a bare-metal + container hybrid with Docker Compose, container security is also very important. Running containers with a non-root user, setting resource limits, and regularly scanning images are included in this scope.
# Basic security headers for Nginx (example)
add_header X-Frame-Options "DENY";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
Conclusion: First Steps and Beyond
Securing a server in the first 45 minutes is just the beginning of my long-term security strategy. This checklist covers the minimum and critical measures I need to take before exposing a VPS to the outside world. Tightening SSH access, configuring the firewall, updating the system, correctly setting user permissions, and establishing basic monitoring mechanisms build a solid foundation.
However, security is an ongoing process. After these initial steps, I focus on topics such as the application’s own security, regular backup and disaster recovery (3-2-1 strategy with solutions like Acronis), more detailed monitoring and log analysis (Grafana, Loki), and layered defense against ransomware. Remember, the best security is to be proactive and continuously update your defenses as threats evolve. Continue to build upon this foundation in your next steps.