So You Got a VPS — Now Stop Leaving the Front Door Open


So you finally spun up a VPS. Congratulations — you are now the proud owner of a tiny computer somewhere in a datacenter, humming quietly, completely exposed to the internet. Like Peter Parker getting bitten by a radioactive spider, great power has found you. The question is whether you'll do something responsible with it, or just let it sit there wide open until some bot in Eastern Europe decides to make it their new crypto miner.

This post walks through the first few layers of hardening I did on this very server — from the initial setup all the way to getting a proper firewall in place. Think of it as the origin story arc. No capes required, but a terminal is non-negotiable.


Step 1 — Update Everything, Then Update Again

Before anything else, your fresh server needs to catch up with the world. Package managers are like that one friend who always has the latest gossip — and the gossip here is security patches.

sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget git unzip ufw fail2ban \
    build-essential software-properties-common apt-transport-https

This pulls in a bunch of tools we'll need later, including ufw (our firewall) and fail2ban (our automated bouncer — more on that another time). Running this on a fresh box is non-negotiable. Think of it as making sure your Avengers team actually has their gear before the battle starts.

After this, set your hostname and timezone. It sounds trivial, but wrong timestamps in logs are genuinely painful when you're trying to trace an incident at 2AM.

sudo hostnamectl set-hostname your-server-name
sudo timedatectl set-timezone Asia/Jakarta

Step 2 — SSH Keys: Stop Using Passwords, Seriously

Here's the thing about SSH passwords — bots scan the entire internet constantly, trying common passwords on port 22. Using a password for SSH in 2026 is roughly the equivalent of leaving the Death Star exhaust port unguarded. We all know how that ended.

The fix is SSH key pairs. The concept is simple: your device holds a private key, the server holds your public key. The private key never travels across the network. If someone doesn't have the private key, they're not getting in — full stop.

Generate your key on your local machine (not the server):

ssh-keygen -t ed25519 -C "yourname@device-name"

ed25519 is the modern choice — smaller, faster, and more secure than the old RSA 2048 keys that were popular a few years ago. When it asks for a passphrase, use one. If your laptop gets stolen, that passphrase is the last line of defence before someone walks into your server with your credentials.

This gives you two files:

~/.ssh/id_ed25519      ← private key — never share this
~/.ssh/id_ed25519.pub  ← public key — goes on the server

Copy the contents of the .pub file onto the server:

# On the server
mkdir -p ~/.ssh
chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys   # paste your public key here
chmod 600 ~/.ssh/authorized_keys

Running multiple devices? Each device gets its own key pair, and you add each public key as a separate line in authorized_keys. If a device goes missing, you delete that one line. Done. No passwords to rotate, no access for anyone else. It's a clean setup.


Step 3 — Locking Down the SSH Daemon

Now that keys are in place, we tell SSH to stop accepting passwords entirely. Edit /etc/ssh/sshd_config:

Protocol 2
Port 2222
MaxAuthTries 3
AllowUsers fath
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
X11Forwarding no
AllowTcpForwarding no

A few things worth calling out here:

Port 2222 — Moving SSH off the default port 22 doesn't stop a determined attacker, but it does filter out a massive amount of automated noise from bots that just sweep port 22 all day. Less noise in your logs means real threats are easier to spot. It's not a security measure on its own — it's a quality-of-life thing.

PermitRootLogin no — Root has infinite power. You do not want bots hammering away at an account that, if compromised, owns everything. Your regular sudo user is fine.

PasswordAuthentication no — This is the big one. Once this is set and your key is working, passwords are gone as an attack vector entirely. The bots can knock all they want; there's no door to unlock.

⚠️ Critical thing here: open a second terminal and confirm you can connect on port 2222 BEFORE closing your current session. Lock yourself out and you're doing a VPS console recovery. Not fun. Like trying to un-snap the Infinity Gauntlet after the fact.

sudo systemctl restart sshd
# New terminal:
ssh -p 2222 youruser@your-server

Step 4 — Firewall with UFW

A server with no firewall is like a building with no walls — technically it has a front door, but anyone can walk in from literally any direction. UFW (Uncomplicated Firewall) is Debian's friendly wrapper around iptables, and it does what it says on the tin.

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 2222/tcp    # SSH — add this FIRST
sudo ufw allow 80/tcp      # HTTP
sudo ufw allow 443/tcp     # HTTPS
sudo ufw enable
sudo ufw status verbose

The order matters: set the SSH rule before enabling UFW. If you flip those steps, you lock yourself out immediately. Ask me how I know. (I don't. Someone else definitely did though.)

Default deny incoming means every port is shut unless explicitly opened. We open exactly three: our custom SSH port, HTTP, and HTTPS. Everything else — rejected. If you're running monitoring tools like Grafana or Loki locally, those ports stay closed too and are accessed via SSH tunnel only.

sudo ufw deny 3000    # Grafana — local only
sudo ufw deny 3100    # Loki — local only

After ufw enable, run sudo ufw status verbose and confirm the rules look exactly as intended before closing any sessions.


Where We Are

At this point the server has:

  • All packages updated
  • A proper hostname and timezone
  • SSH key authentication only, password login disabled, running on a non-default port
  • A firewall that denies everything not explicitly allowed

That's a solid foundation. Not invincible — no server ever is — but we've closed the obvious doors that most bots and opportunistic scanners target. The next posts will go deeper: web server hardening, SSL, a honeypot that traps and bans admin-hunters, and a full alerting stack that pings you on Telegram when anything interesting happens.

The fun parts are coming. Stay tuned.

Previous Next