What You'll Need
This whole setup-and-hardening pass takes about 20–25 minutes. Have these ready:
- A VPS running Ubuntu (24.04 LTS in these screenshots) and its public IP address
- The root password your provider emailed you (you'll retire it by the end)
- A terminal on your own machine — PowerShell/Windows Terminal, macOS Terminal, or any Linux shell
- 20 minutes and a willingness to copy/paste carefully
thapa with your own username and the example IP 62.72.59.218 with your server's real IP.
Spin Up Your VPS
Order a VPS, pick Ubuntu 24.04 as the operating system, and choose a data-center region close to your users (this one is in Mumbai). Once it boots, your provider's dashboard shows the two things you need: the public IP address and the root password.
The public IP is your server's address on the internet — you'll SSH to it now, and later point your domain's DNS at it. Keep this dashboard tab open; the Reboot VPS button here is your safety net if you ever lock yourself out.
Log In Over SSH as Root
SSH (Secure Shell) is the protocol you use to securely control a remote server. The ssh client ships by default on Linux, macOS and Windows. Open a terminal on your own computer and connect using the server's IP — here root is the username Hostinger gave us (yours may differ), and the part after @ is the IP:
ssh root@62.72.59.218
The very first time, SSH asks you to verify the server's fingerprint. Type yes to trust it — it's saved on your machine and checked on every future connection:
Enter the root password from your dashboard when prompted. You're now inside the server — the prompt changes to something like root@srv416761:~#.
On later connections SSH re-validates that saved fingerprint. If it suddenly doesn't match and you're blocked, treat it as a red flag — the server may have been tampered with or you're connecting to an impostor.
Update & Upgrade Packages
The first thing to do on any new server is refresh the package list and upgrade everything that's outdated. Ubuntu is Debian-based, so we use APT:
apt update
apt upgrade -y
apt update only refreshes the list of available packages from the repositories — it doesn't change anything installed. apt upgrade then installs the newer versions. On a Red Hat–based distro (CentOS, Fedora) you'd swap apt for dnf.
Some upgrades (like a new kernel) need a reboot. Check for the marker file — if it exists, a reboot is required:
cat /var/run/reboot-required
You can reboot straight from your provider's dashboard (the Reboot VPS button you saw earlier), or over SSH:
reboot
Create a Non-Root Sudo User
Living as root full-time is risky — one wrong command runs with unlimited power. Create a regular user instead. You'll be asked to set a password (make it different from root's) and can press Enter through the optional details:
adduser thapa
By default this user can't run admin commands. Add it to the sudo group so it can — only when prefixed with sudo — then confirm:
usermod -aG sudo thapa
groups thapa
Seeing thapa : thapa sudo users confirms it worked. Exit and log back in as the new user:
ssh thapa@62.72.59.218
As a normal user you no longer have automatic admin powers. Run an admin command directly and you'll hit a wall — that's the security working as intended:
The fix is simply to prefix admin commands with sudo and enter your user's password when asked:
sudo apt update
If an app or a leaked credential gets compromised, a non-root user limits the blast radius — the attacker can't touch the whole system without your sudo password. It's the single highest-value habit in server security.
Set Up SSH Key Authentication
Passwords can be brute-forced; SSH keys effectively can't. On your own machine (not the server), generate a modern ed25519 key pair. Press Enter to accept the default location and optionally set a passphrase:
ssh-keygen -t ed25519 -C "you@example.com"
# On a legacy system without ed25519 support, use RSA:
ssh-keygen -t rsa -b 4096 -C "you@example.com"
Keys are stored in your .ssh folder (C:\Users\You\.ssh on Windows, ~/.ssh on macOS & Linux). You'll see two files — the distinction is critical:
| File | What it is | Rule |
|---|---|---|
id_ed25519 | Private key | Never share it. Stays on your machine. |
id_ed25519.pub | Public key | Safe to copy — this is what goes on the VPS. |
Now put the public key on the server. The easiest way is ssh-copy-id (macOS, Linux and Git Bash):
ssh-copy-id thapa@62.72.59.218
If you'd rather do it by hand (or you're on plain Windows PowerShell): connect as the non-root user, create the .ssh folder, and paste your public key into an authorized_keys file:
mkdir -p ~/.ssh
nano ~/.ssh/authorized_keys
# paste the full contents of id_ed25519.pub, then Ctrl+O, Enter, Ctrl+X
Now test it — you should log straight in with no password prompt (or just your key's passphrase):
ssh thapa@62.72.59.218
Enable the OpenSSH Agent (Windows Only)
If you set a passphrase on your key, the OpenSSH Authentication Agent can remember it so you don't retype it every connection. The quickest way is two commands in PowerShell (as administrator):
Get-Service -Name ssh-agent | Set-Service -StartupType Automatic
Start-Service ssh-agent
Prefer a GUI? Open Services from Windows Search, find OpenSSH Authentication Agent, right-click it and choose Properties:
Set Startup type to Automatic, click Start, then OK:
Then add your key to the agent (only needed if you used a non-default key name or a passphrase):
ssh-add $env:USERPROFILE\.ssh\id_ed25519
You can skip this step — the SSH agent is already running. Just run ssh-add ~/.ssh/id_ed25519 if you want your passphrase cached for the session.
Disable Password & Root Login
With key login confirmed, it's time to slam the door on passwords and root. On the server, open the SSH daemon config:
sudo nano /etc/ssh/sshd_config
Find these lines, uncomment them if needed, and set them like so — this keeps key auth on and turns password auth off:
PubkeyAuthentication yes
PasswordAuthentication no
On Hostinger that file is 50-cloud-init.conf inside /etc/ssh/sshd_config.d/ (note: sshd_config.d, not ssh_config.d). Look at it:
cd /etc/ssh/sshd_config.d/
ls
sudo cat 50-cloud-init.conf
Edit it and change its yes to no so it stops overriding you:
sudo nano /etc/ssh/sshd_config.d/50-cloud-init.conf
# set: PasswordAuthentication no
Now forbid root from logging in over SSH entirely. Back in the main config, find PermitRootLogin and set it to no:
PermitRootLogin no
Save each file (Ctrl+O, Enter, Ctrl+X), then restart SSH to apply everything:
sudo systemctl restart ssh
# the service may be named sshd on CentOS/Fedora
Keep your current SSH window open. In a new terminal, confirm you can still log in as thapa with your key, and that ssh root@<ip> is now refused. If the new login works, you're safe to close the old window.
Put Up a UFW Firewall
A firewall closes every port except the ones you explicitly open. Ubuntu (and Hostinger) ship with UFW, the Uncomplicated Firewall. If yours doesn't have it: sudo apt install ufw. Check its current state:
sudo ufw status
It'll likely report inactive. Set a default-deny posture — block all incoming, allow all outgoing:
sudo ufw default deny incoming
sudo ufw default allow outgoing
Allow SSH so you can keep managing the server (if you changed the SSH port, use sudo ufw allow <port-number> instead):
sudo ufw allow OpenSSH
We'll be hosting websites later, so open the web ports too, then turn the firewall on:
sudo ufw allow http # same as 80/tcp
sudo ufw allow https # same as 443/tcp
sudo ufw enable
Finally, confirm exactly what's open:
sudo ufw status
active with only the ports you intended? Your server is now hardened — key-only login, no root over SSH, and a default-deny firewall. 🎉
Your Hardening Checklist
Here's everything you just locked down, at a glance:
| Done | Hardening step |
|---|---|
| ✅ | Updated & upgraded all packages |
| ✅ | Created a non-root user with sudo rights |
| ✅ | Switched to SSH key authentication (ed25519) |
| ✅ | Disabled password login — including the cloud-init override |
| ✅ | Disabled root login over SSH |
| ✅ | Enabled a default-deny UFW firewall with only SSH/HTTP/HTTPS open |
Quick Troubleshooting
| Symptom | Likely fix |
|---|---|
| "Permission denied (publickey)" | Key isn't in ~/.ssh/authorized_keys, or permissions are wrong — the file must be 600 and ~/.ssh must be 700. |
| Still being asked for a password | The 50-cloud-init.conf override is still set to yes — fix it and sudo systemctl restart ssh. |
| Locked out completely | Use your provider's browser terminal / VNC console to get in and undo the change. |
| Firewall blocking a service | Open its port: sudo ufw allow <port>/tcp, then re-check sudo ufw status. |
Wrapping Up
A default VPS is an open invitation; a hardened one is a fortress. In one short session you updated the system, replaced password root login with key-only access for a limited sudo user, neutralised the cloud-init password override that catches almost everyone, and wrapped the whole thing in a default-deny firewall. That's the exact baseline every production server should start from — do it once, and every app you deploy afterwards inherits it.
Want to learn this end to end with real projects and mentorship? Our Full Stack AI Bootcamp covers building and deploying production apps on your own infrastructure.
Discussion