~/im/blog
Hire Me

Let's Talk

Interested in working together or have a question? I'm always open to discussing new projects.

Get in touch

Connect

Find me on social media and professional networks.

Privacy PolicyTerms of Conditions
© 2026 Irfan MiralDeveloped byirfanMiral.com
HomeAbout/ResumeBlogContact
2026-06-10• 5 min read

The First Hour on a New VPS: My Hardening Checklist

Security Server Security Linux Administration VPS

When a new VPS comes online, whether it's for a client or one of my own boxes, nothing gets deployed until it's been through the same short checklist. It takes under an hour, and it closes off the attack surface that automated scanners and bots go after within minutes of a new IP showing up on the internet. None of this is exotic; it's the boring stuff that actually matters.

1. Get off password auth, immediately

The first thing I do after the initial root login is create a non-root user with sudo access, copy my SSH public key over, and test that key-based login works in a second terminal before touching anything else.

adduser deploy
usermod -aG sudo deploy
rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy

Once that login works, /etc/ssh/sshd_config gets three changes:

PermitRootLogin no
PasswordAuthentication no
MaxAuthTries 3

systemctl restart sshd, and from this point on the only way into the box is a private key that exists on my machine and the client's, never a password that can be brute-forced or phished.

2. Default-deny firewall

UFW (or CSF, depending on the stack) goes on next, set to deny all incoming traffic by default with only the ports the server actually needs opened explicitly:

ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

Anything else, a database port, a control panel, an admin interface, gets added only when something specific needs it, and usually scoped to a specific source IP or a VPN range rather than left open to the world. This is the step that would have stopped the CyberPanel worm I wrote about earlier cold: the vulnerable admin port was reachable from anywhere, and the firewall rule to fix that takes about ten seconds.

3. Fail2ban for the noise

Even with key-only SSH, logs fill up with login attempts from bots scanning IP ranges. Fail2ban doesn't stop a targeted attack, but it cuts the noise dramatically and bans IPs after a handful of failed attempts:

apt install fail2ban
systemctl enable --now fail2ban

The default sshd jail is enough to start. If the server is also running a web-facing service like Nginx or a WordPress login form, I add jails for those too, repeated /wp-login.php hits from the same IP get banned just as fast as SSH brute-forcing.

4. Unattended security updates

A server that doesn't patch itself eventually runs a kernel or OpenSSL version with a known exploit sitting on it, waiting. Unattended upgrades handle security patches automatically, with reboots scheduled for a low-traffic window if a kernel update needs one:

apt install unattended-upgrades
dpkg-reconfigure --priority=low unattended-upgrades

I check /var/log/unattended-upgrades/ once in a while just to confirm it's actually running, not just installed.

That's the baseline

None of this is advanced, and that's the point, it's the floor every server should sit on before anything application-specific goes on top. The servers I've seen get compromised almost never fell to a zero-day; they fell to a default password, an open admin port, or a kernel that hadn't been patched in months. An hour spent here saves a much worse day later.

PreviousPostgreSQL Backups You Can Actually Restore: Setting Up pgBackRest