VPS Hardening Script for Ubuntu and Debian — ironboot

A guided bash script that hardens a fresh Ubuntu or Debian VPS in 12 steps — non-root user, SSH hardening, UFW firewall, fail2ban, Tailscale, Docker, and auto-updates. One run, clean server.

By David SharkeyPublished on March 29, 2026
ironboot VPS hardening script running on Ubuntu terminal
VPS Security
DevOps
Ubuntu
Debian
Tailscale
fail2ban
Linux
Tools

TL;DR

ironboot is a bash script that hardens a fresh Ubuntu or Debian VPS in 12 guided steps — non-root user, SSH hardening, UFW firewall, fail2ban, Tailscale, Docker, and automatic updates. Every step is optional, skippable, and logged. One run, clean server.

Every fresh VPS ships in an insecure state. Port 22 open. Running as root. No firewall. Password authentication enabled. No fail2ban. This is the default — and on most servers, it stays that way.

I built ironboot to fix that. It's a guided bash script that hardens a fresh Ubuntu or Debian VPS in 12 steps. You answer questions, it applies changes, it logs everything. One run. If you're spinning up a Hetzner box for OpenClaw, a side project, or anything else worth protecting — this is the VPS hardening script I use on every server I own.


Why most VPS servers are insecure by default

Most people setting up a VPS know, in principle, what they should do. Non-root user. Closed ports. SSH keys only. They don't do it because doing it properly takes an afternoon. Research the right sysctl values. Find a current UFW guide. Not lock yourself out of SSH while changing the port. Repeat for the next server.

The cost of skipping it is usually invisible — right up until it isn't.

Port 22 is scanned constantly. Within minutes of a fresh VPS going live, automated bots are hitting port 22 testing credential combinations. Moving SSH off port 22 won't stop a targeted attack, but it eliminates thousands of background attempts per day that clog your logs and burn fail2ban cycles for no reason.

Running as root means one wrong command is catastrophic. A non-root user with sudo means you have to consciously escalate privilege for anything that could do real damage. Smaller blast radius. Auditable access. When something goes wrong — and it will — you have a cleaner path to understanding what happened.

Databases and internal services bind to ports they shouldn't. Without a default-deny firewall, everything your server is listening on is reachable from the internet. UFW with default-deny means only ports you explicitly open are reachable, regardless of what gets installed or misconfigured later.


VPS security checklist — what ironboot covers

A quick reference before the full breakdown. These are the 12 steps ironboot walks through on a fresh Ubuntu or Debian server:

  1. Create a non-root admin user with sudo
  2. Harden SSH — port, root login, password auth, session settings
  3. Apply kernel network hardening via sysctl
  4. Set up UFW firewall with default-deny incoming
  5. Install and configure fail2ban
  6. Set up Git and GitHub deploy key access
  7. Install Tailscale (optional but strongly recommended)
  8. Optionally close public SSH once Tailscale is confirmed working
  9. Install Docker from the official repository
  10. Enable automatic security updates (unattended-upgrades)
  11. Schedule weekly maintenance — apt, Docker image pulls, prune
  12. Run automated verification checks

Every step is optional. Skip anything with --skip, run a specific one with --only. Nothing happens irreversibly without a warning first.


What each step does and why it matters

Step 1 — Non-root admin user

Creates a named user, adds them to sudo, sets up their ~/.ssh directory, and optionally copies root's authorized_keys across. That last part matters: if your SSH key already works for root, copying it to the new user means you can log in immediately and then safely disable root SSH access in Step 2 without risking a lockout.

Step 2 — SSH hardening

Moves SSH off port 22, disables root login, disables password authentication, and applies hardening defaults: MaxAuthTries 3, LoginGraceTime 30, X11Forwarding off. Before restarting SSH, the script validates the config with sshd -t. A syntax error will not cut off your session. Keep your current terminal open and verify the new login in a second window before closing anything.

Step 3 — Kernel network hardening

Writes /etc/sysctl.d/99-vps-bootstrap.conf and applies it. SYN flood protection, ICMP redirect blocking, reverse path filtering, source route rejection. These are settings that cloud images ship at unsafe defaults for a public-facing server. None of them affect normal operation.

Step 4 — UFW firewall

Installs UFW with default-deny incoming. SSH gets allowed on the active port before the firewall is enabled — the ordering is handled automatically so you can't accidentally lock yourself out. Rate limiting on the SSH port: 6 or more connection attempts within 30 seconds triggers a block.

Step 5 — fail2ban

Bans source IPs after 3 failed auth attempts within 10 minutes. Three-hour bans, enforced through UFW. Even with password auth disabled, fail2ban keeps scans down and logs readable. Three retries is right — a legitimate user failing key auth more than three times has a config problem that extra attempts won't fix.

Step 6 — Git and GitHub access

Installs git, generates an ed25519 keypair for the admin user, adds GitHub to known_hosts. A server with its own deploy key means you can revoke access independently without touching your personal key.

Step 7 — Tailscale

This is the step I'd call non-negotiable for anything I run personally. Tailscale creates a private encrypted WireGuard network between all your devices. Your server gets a stable private IP and hostname. tailscale ssh user@hostname works from any device on your Tailnet — laptop, phone, wherever. With Tailscale SSH enabled, the server's public SSH port doesn't need to exist at all.

Step 8 — Optional public SSH closure

If Tailscale SSH was enabled in Step 7, this step offers to remove the UFW rules that allow public SSH access. The server is then reachable only through your Tailnet. Only say yes after you've confirmed tailscale ssh user@hostname works in a second terminal. That test is not optional.

Steps 9–11 — Docker, auto-updates, scheduled maintenance

Docker Engine from the official repository — not Ubuntu's default apt sources, which can be years out of date. Unattended-upgrades configured for daily security patches with auto-reboot disabled. A weekly maintenance script that runs a full apt upgrade, pulls updated images for running containers, and prunes accumulated cruft. All logged.

Step 12 — Verification

Automated checks after setup: SSH config validation, UFW status, fail2ban status, Docker service state, Tailscale connection. Results go into the audit log. This doesn't replace testing manually. Open a new terminal and verify your own access path before closing the original session.


Recommended stack for a fresh VPS

If you're starting from scratch, this is what I use:

Host — Hetzner The best value in cloud hosting. A 2–4 core VPS handles OpenClaw, multiple web apps, or a dozen side projects without breaking a sweat. I run sharkey.io and about a dozen other things on a single Hetzner box. The pricing is a fraction of AWS or DigitalOcean for equivalent specs.

Private network — Tailscale Set it up on every device you own and every server you run. It creates an encrypted mesh network over WireGuard. Once configured, SSH is as simple as tailscale ssh user@hostname from anywhere. No exposed ports, no managing SSH keys across devices, no VPN config. I'd consider it non-negotiable.


Who this VPS hardening script is for

Anyone spinning up a fresh VPS who doesn't want to spend an afternoon researching what they should be doing — or worse, skipping it entirely because it's tedious.

Good fit: fresh Hetzner, DigitalOcean, Vultr, or similar VPS. Personal production servers. Team servers where you want everyone starting from the same hardened baseline.

Not the right tool for existing production servers with custom configurations. Run --dry-run and review every step before applying anything to a server that's already live.

curl -O https://raw.githubusercontent.com/dshark3y/ironboot/main/vps-bootstrap-v1.6.2.sh
sudo bash vps-bootstrap-v1.6.2.sh

Built for my own infrastructure. Sharing it because this is table stakes for any server with something real on it.


Frequently asked questions

Is it safe to run ironboot on an existing production server?

Not without caution. ironboot is designed for fresh installs. On an existing server, run sudo bash vps-bootstrap-v1.6.2.sh --dry-run first and review every proposed change before applying anything. Some steps may conflict with your current configuration.

What's the difference between UFW and fail2ban — do I need both?

They do different things. UFW is a firewall that controls which ports are reachable. fail2ban monitors authentication logs and blocks IPs that fail repeated logins. UFW handles the structural rules; fail2ban handles the dynamic threat response. You want both.

Do I need Tailscale to use ironboot?

No — Tailscale is optional. Steps 7 and 8 can be skipped entirely if you don't want it. But if you're serious about server security, Tailscale SSH is the cleanest solution available: it removes public SSH exposure entirely without adding operational complexity.

How do I secure an OpenClaw instance on a VPS?

Run ironboot first on a fresh Ubuntu or Debian VPS before deploying OpenClaw. The most important steps are: non-root user (Step 1), SSH hardening (Step 2), UFW firewall (Step 4), and fail2ban (Step 5). If you're running OpenClaw behind a reverse proxy, also make sure Docker isn't bypassing your UFW rules — Docker's iptables integration can expose ports that UFW thinks are closed.

What happens if I lock myself out during SSH hardening?

ironboot validates the SSH config with sshd -t before restarting the service — a syntax error won't cut your connection. For firewall and SSH port changes, the script explicitly warns you to open a second terminal and verify the new login before closing the original session. Most cloud providers also offer a web-based console as a recovery path if something does go wrong.


Nobody gets hacked slowly.