When you provision a fresh VPS, you’re handed a bare system. No firewall, no dedicated user, direct root access, and temporary passwords. Leave it in that state, and automated bots will find it within the hour. I’ve seen infrastructure logs with 2,000 endpoints registering brute-force attempts within minutes of the first boot.
Configuring a Linux VPS from scratch requires a methodical approach. You can’t wing it. Every step builds on the previous one. Forget the firewall, and your SSH service sits exposed. Skip updates, and a known CVE opens the door to privilege escalation. This is the checklist I use to harden any Ubuntu or Debian server before installing a single production service.
Initial Root Access
Your provider sends the public IP and a temporary root password. Connect immediately using the IP address rather than a domain, as DNS might not have propagated yet.
ssh root@IP_DEL_VPS
If it’s your first connection, SSH will ask you to verify the server fingerprint. Accept it. Change the password right away if the provider doesn’t force you to on the first login. Pick a long, complex string, even though we’ll disable remote password authentication shortly.
Update the System
The provider’s template might be running packages that are months out of date. Before exposing any services, apply all security patches.
apt update && apt upgrade -y
This updates the package index and installs the latest available versions. If the kernel gets updated, schedule a reboot. On a freshly provisioned VPS, a reboot won’t cause any downtime.
Create a Non-Root User with Sudo
Working as root is a gamble. A single typo in a command can wipe out the entire filesystem. Create a dedicated user for daily operations.
adduser deploy
The command prompts for a password and some optional data. Set a strong password. Now, add the user to the sudo group so they can execute commands as root when needed.
usermod -aG sudo deploy
Verify that the user can use sudo. Switch to the user and test:
su - deploy
sudo whoami
The output should be root.
Configure SSH (Public Key, Disable Root, Change Port)
Password-based SSH access is the most common attack vector. Bots constantly run through entire credential dictionaries on port 22. You need to shut this down.
Generate an SSH key on your local machine, not on the server:
ssh-keygen -t ed25519 -C "deploy@vps-prod"
Copy the public key to the VPS. Use ssh-copy-id, specifying the correct user:
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@IP_DEL_VPS
You can now log in without a password. Test the access, then modify the SSH configuration.
sudo nano /etc/ssh/sshd_config
Find and modify these parameters:
Port 2222
PermitRootLogin no
PasswordAuthentication no
Choose a high port like 2222 or 4422. Stay away from 22. Restart the SSH daemon to apply the changes:
sudo systemctl restart sshd
Before closing your current root session, open a new terminal window and verify that you can log in with the new user on the new port. Never close your original session until you’ve confirmed the new access works. [Read also: Hardening SSH on Linux: 10 Essential Settings]
Configure UFW
The firewall is your perimeter. On Ubuntu, UFW simplifies iptables management. Allow only the traffic you need, starting with SSH.
sudo ufw allow 2222/tcp
sudo ufw enable
Remember to allow your new SSH port. If you enable UFW without allowing your port, you’ll lock yourself out. Check the status:
sudo ufw status
The output should show port 2222 allowed. Later, when you install Nginx or a database, you’ll add ports 80, 443, or 5432 the same way.
Install fail2ban
Even with SSH keys, bots will keep knocking. Fail2ban parses system logs and bans IPs after repeated failed login attempts.
sudo apt install fail2ban -y
The default configuration on Ubuntu is sufficient to get started. The filter reads the SSH log file and blocks IPs after 5 failed attempts for 10 minutes. Verify the service is active:
sudo systemctl status fail2ban
If you changed your SSH port, make sure fail2ban is reading the correct logs. The default configuration on Debian and Ubuntu automatically detects the listening port.
Enable Automatic Security Updates
An unpatched server is a compromised server. You can’t log into every machine daily to check for patches. Configure unattended-upgrades to install only security updates automatically.
apt install unattended-upgrades -y
dpkg-reconfigure unattended-upgrades
Answer Yes to the automatic installation prompt. The system will download and apply security patches nightly. It won’t install feature updates or libraries that might break compatibility. Check the official documentation at wiki.debian.org/UnattendedUpgrades to customize package sources.
Configure Timezone and Hostname
Logs are useless if the timestamps are wrong. If your server is running in the wrong timezone, correlating events across different systems becomes a nightmare. Set the correct timezone:
timedatectl set-timezone Europe/Rome
Also, set a descriptive hostname. Don’t leave the generic name assigned by the provider.
hostnamectl set-hostname mio-server
Add the hostname to the /etc/hosts file, mapping it to 127.0.1.1 to avoid sudo warnings.
NTP Synchronization
System clock drift causes issues with TLS certificates, Kerberos authentication, and database replication. Enable NTP synchronization.
sudo timedatectl set-ntp true
Verify the synchronization status:
timedatectl status
Make sure the NTP synchronized field reports yes. On Ubuntu 22.04 and 24.04, timesyncd handles synchronization efficiently without needing to install ntpd.
Final Checklist — Verify Everything
Before declaring the VPS ready, run a cross-check. Verify that every critical service is active and that there are no suspicious logins.
Check SSH status:
sudo systemctl status sshd
Check fail2ban status:
sudo systemctl status fail2ban
Verify firewall rules:
sudo ufw status
Finally, check the last logins to the system. This command shows the last 10 recorded sessions:
last -n 10
You should only see your deploy user and your IP. If you see root or unknown IPs, investigate immediately.
Next Steps (Nginx, Database, App)
Your VPS is now solid and isolated from the outside. You have a secure foundation to build on. The next step depends on your workload. If you’re hosting a web application, install Nginx, open ports 80 and 443 on UFW, configure the server block, and add Certbot for Let’s Encrypt certificates. If you need a database, install PostgreSQL, bind listening to 127.0.0.1, and set scram-sha-256 authentication. Every new service added requires its own specific security configuration. Never skip this step.