A secure VPS is built in the first hour after you deploy it, before you install anything else. Internet-facing servers start receiving automated SSH login attempts within minutes of getting a public IP, and every service you add later is easier to protect on a hardened base. This checklist takes a fresh Ubuntu or Debian server through the essentials in order: updates, a non-root user, key-only SSH, a firewall, fail2ban and automatic security updates.
It assumes you deployed with an SSH key. If you have not created one yet, follow how to create SSH keys and log in to your VPS first. For choosing the region itself, see the VPS locations guide.
Before you start: keep a way back in
Hardening SSH is the one step that can lock you out. Two habits prevent that:
- Keep your current SSH session open while you change SSH settings, and test with a second terminal before closing the first.
- Know your fallback. On Cloudj servers you can open an SSH console in the browser from the server page in the console, and you can reset the root password there if needed.
Commands below are run as root on first login, and with sudo after you create your own user. Replace deploy with the username you want.
Minute 0 to 10: update the system
Install current packages before anything else, so you are not hardening a system with known holes.
apt update
apt full-upgrade -y
If the upgrade installed a new kernel, reboot now while nothing important is running. On Ubuntu the file /run/reboot-required exists when a reboot is needed; on Debian, reboot whenever the upgrade included a linux-image package.
ls /run/reboot-required
reboot
While you wait, check the hostname and time zone. Correct time matters for logs and for TLS:
timedatectl
Minute 10 to 20: create a sudo user
Working as root all day turns every typo into a system-wide mistake. Create a normal user with sudo rights:
adduser deploy
usermod -aG sudo deploy
Copy root's authorized SSH keys to the new user so you can log in with the same key:
install -d -m 700 -o deploy -g deploy /home/deploy/.ssh
install -m 600 -o deploy -g deploy ~/.ssh/authorized_keys /home/deploy/.ssh/authorized_keys
Now open a second terminal on your computer and confirm the new login works, including sudo:
ssh [email protected]
sudo whoami
The last command should print root. Do not continue until this works.
Minute 20 to 30: secure VPS SSH access
With key login confirmed for your new user, turn off password logins and direct root login. Use a drop-in file instead of editing the main config, so package upgrades do not overwrite your change.
One detail matters here: OpenSSH uses the first value it reads for each setting, and drop-in files are read in alphabetical order. Some cloud images ship a file such as 50-cloud-init.conf that turns password login on, so name yours to sort first:
sudo tee /etc/ssh/sshd_config.d/00-hardening.conf > /dev/null <<'EOF'
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
EOF
Check the syntax, confirm the effective values, then restart SSH:
sudo sshd -t
sudo sshd -T | grep -Ei 'permitrootlogin|passwordauthentication|kbdinteractive'
sudo systemctl restart ssh
The service is called ssh on both Ubuntu and Debian. Keep your existing session open and test a fresh login from another terminal. Then confirm that root is refused:
ssh [email protected]
You should see Permission denied (publickey). That is the result you want.
Changing the SSH port away from 22 is optional. It reduces log noise but adds little real security once passwords are off; if you change it, open the new port in the firewall before restarting SSH.
Minute 30 to 40: turn on a firewall
ufw is the simplest host firewall on Ubuntu and Debian. Ubuntu usually has it installed; on Debian, install it first. Allow SSH before enabling, or you will cut your own session:
sudo apt install ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw enable
sudo ufw status verbose
Open only what the server actually serves. For a web server that usually means:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
Databases, admin panels and caches should not be open to the internet. Bind them to 127.0.0.1 or allow them only from specific IPs. Our ufw firewall guide goes deeper into writing rules. If you plan to run containers, read the firewall section in installing Docker on a VPS, because Docker's published ports bypass ufw rules by default.
Minute 40 to 50: block repeated login attempts with fail2ban
With passwords off, brute force cannot succeed, but fail2ban still cuts log noise and slows scanners by banning IPs that fail repeatedly.
sudo apt install fail2ban python3-systemd
Create a local jail file. Using the systemd backend works on both Ubuntu and newer Debian releases, where SSH logs may live only in the journal:
sudo tee /etc/fail2ban/jail.local > /dev/null <<'EOF'
[sshd]
enabled = true
backend = systemd
maxretry = 5
findtime = 10m
bantime = 1h
EOF
sudo systemctl enable --now fail2ban
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd
If you lock yourself out from your own IP by mistake, use the browser console to unban it with sudo fail2ban-client set sshd unbanip YOUR_IP.
Minute 50 to 60: automatic security updates
Security fixes only help when they are installed. unattended-upgrades applies them daily:
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
Answer "Yes" to enable automatic updates. By default it installs security updates only, which is the safe choice for most servers. You can check what it did in /var/log/unattended-upgrades/.
The checklist at a glance
| Step | Command or setting | Done when |
|---|---|---|
| Update | apt full-upgrade | No pending updates, rebooted if needed |
| Sudo user | adduser, usermod -aG sudo | sudo whoami prints root |
| SSH hardening | 00-hardening.conf | Root and password logins refused |
| Firewall | ufw with SSH allowed | ufw status shows only needed ports |
| fail2ban | [sshd] jail, systemd backend | fail2ban-client status sshd works |
| Auto updates | unattended-upgrades | Enabled in dpkg-reconfigure |
After the first hour
Security does not end here. Plan backups before you have data worth losing; the VPS backup strategy guide covers options. Keep an eye on CPU, RAM and network use in the console metrics so unusual activity stands out. And if you run a web app, put it behind a reverse proxy with TLS rather than exposing application ports directly.
FAQ
Is a firewall needed if only SSH is running?
It is still worth it. A default-deny firewall protects you when you later install a service that listens on all interfaces without you noticing.
Should I disable root login if I use SSH keys?
Yes. A named sudo user leaves a clearer audit trail, and refusing root removes the most targeted username from the attack surface.
What if I lock myself out?
Use the browser SSH console on the server page in the Cloudj console to log in and fix the configuration, or reset the root password there.
Does this apply to Windows servers?
No. Windows servers on Cloudj use password login and need Windows-specific hardening such as strong passwords, Windows Update and restricting RDP access.
Start on a clean server
Deploy a fresh Ubuntu or Debian server in the Cloudj console with your SSH key attached, and work through this list before anything else. You can compare locations on the cloud servers page and see live prices on the pricing page.