Bootstrapping & Hardening a Fresh VPS — My “Hey Lorna” baseline for VPS Security

Bootstrapping & Hardening a Fresh VPS — My “Hey Lorna” Baseline for VPS Security

When it comes to VPS security, having a repeatable, routine way of ensuring your server stays safe is key. I shared a Gist on Github a little while back that allows users with a new virtual private server to set it up quickly and safely.

But first of all, you might be thinking, ‘Who’s Lorna?’ I’ve written before about the argument for naming your machines, and this is how Lorna was born. ‘Hey Lorna’ is the name of my Life OS, which lives on my VPS. You can read about that here.

When I spin up a fresh VPS (Virtual Private Server), I want security, convenience, and repeatability — without over-engineering. This shell-script is my go-to for initializing a server: set time-zone, install essentials, lock it down a bit, enable Docker for deployments. It’s simple, opinionated, and safe enough for many small projects.

Here’s a breakdown of what the script does, why it matters — and how you can run it to start from a secure baseline on Ubuntu. For reference, I developed this script on Ubuntu version 24.04.

Why This Matters

Unsecured servers get probed within minutes. Simple misconfigurations — unused ports, password SSH, outdated packages — can make a VPS an easy target for bots or attackers. Taking a little time up front to harden your server drastically reduces your risk of trouble down the line.

This script implements many of the recommended practices in the Linux sysadmin world: firewall, SSH-key authentication, automated updates, minimum exposed ports, swap for stability, and Docker for flexible deployments.

What the Script Does (Step-by-Step)

  1. Set timezone & hostname — useful for logs and clarity when you SSH in.
  2. System update & install base packages — ensures you start with latest security patches; installs useful tools: shell utilities, networking tools, package tools.
  3. Swapfile creation (if none present) — ensures the server has swap, which helps stability on small VPS plans.
  4. Firewall with ufw:
    • resets previous firewall rules, then sets defaults
    • blocks all incoming by default, allows outgoing
    • explicitly permits ports 22 (SSH), 80 (HTTP), 443 (HTTPS) — ideal for a typical web server or deployment environment.
  5. Install & enable fail2ban with a basic SSH jail — helps block brute-force login attempts automatically.
  6. Enable unattended security upgrades — ensures that security updates get applied without manual intervention.
  7. Tweak sysctl for vm.max_map_count — sometimes needed for containerized workloads (e.g. Elasticsearch), but harmless for most servers.
  8. SSH hardening and admin user creation:
    • creates a non-root “admin” user with sudo privileges
    • installs your public SSH key for that user
    • disables root login and password-based SSH (only if key is provided) — forces key-only login.
  9. Install docker-ce and docker-compose plugin — prepares the server for container deployments. Adds the admin user to the docker group for ease-of-use.
  10. Basic MOTD / summary output — gives a quick overview of the server’s status (timezone, docker status, UFW status, Fail2Ban) when you SSH in.

At the end: you have a hardened, minimal server ready to receive deployments — with SSH locked down, updates automated, resources in place, Docker ready.

How to Use It

The full script is below, followed by usage instructions. You can also check out the original Github Gist here.

Follow the steps below to use the script – it runs fairly quickly, but depending on the specs of your VPS some steps may take a little while.

  1. Clone or copy the script to your local machine.
  2. Edit the top of the script to replace placeholders:
    • ADMIN_USER → your desired non-root username
    • ADMIN_SSH_KEY → your public SSH key (one line)
    • NEW_HOSTNAME → a name for your server (e.g. “my-vps-1”)
    • TIMEZONE → as appropriate (default is Europe/London)
    • SWAP_SIZE_GB → adjust based on your server’s RAM / expected load
  3. Upload or paste the script to your VPS (e.g. via scp or copy/paste).
  4. Run it with root (or via sudo).
  5. Once it finishes, log out and log back in as your new admin user via SSH key — confirm all is working.

Note that your for step 5, your login from now on will be ADMIN_USER@NEW_HOSTNAME – replace with your values.

What This Doesn’t Do (and Options to Consider)

This baseline is safe and straightforward — but not hardcore. Depending on your threat model or workload you may want to go further:

  • Change SSH port (from 22 → non-standard) to avoid some bot-noise.
  • Lock down or disable IPv6 (if not used), tweak more kernel / network sysctls for stricter security.
  • Remove unnecessary services or packages — anything not needed (mail, old daemons, compilers, etc.) to reduce attack surface.
  • Add file-integrity monitoring, or intrusion detection (e.g. AIDE, Tripwire, or external monitoring) for more advanced security.
  • Backups or snapshotting — the script doesn’t handle backups. Important for production. I use Dokploy as my PaaS – and backup to S3 storage using the internal tools.
  • Log monitoring or external syslog — helps catch suspicious behaviour that fail2ban might miss.
  • User and permission audits — especially if more people will access server.

Depending on your needs, you might build a “hardening-v2” script on top of this.

This script is intended as a simple, repeatable starting point — a “secure default” server you can reliably deploy without fuss. For many personal projects, small web apps, or container-based services, this baseline is already a big upgrade over leaving a default VPS open on every port, with password SSH, and no firewall.


Considering a VPS, or looking to change provider?

I recommend Fasthosts. I’ve been using Fasthosts for a number of internet services for a long time, and I couldn’t be happier with the service, uptime, and price. Find out more here.


Security is a process, not a checkbox. Think of this as “Stage One: Locking the front door and putting up the first fence.” Over time you may layer more fences, alarms, cameras. But you’ve started strong.

If you decide to build on this (hardening-v2, backups, IPv6 lockdown, intrusion detection) — I’d be happy to help you sketch that out too. Get in touch!

About Luke Dunsmore

Hey, I’m Luke…

I design, build, and tinker with the web — from WordPress and Shopify to self-hosted tools on my “Hey Lorna” server. This blog is where I share the experiments, lessons, and odd little projects that shape my work.

Start Here ⬇️

Shopify vs Etsy vs WordPress for Selling Physical Goods — An Honest Breakdown

Shopify vs Etsy vs WordPress for Selling Physical Goods — An Honest Breakdown

Portfolio

Luke Dunsmore - Just a Shop, Sir Custom Freddie Funko Steampunk Graphic Design

See My Work →

Labs

Luke Dunsmore - Luke Dunsmore Labs