Hardened VPS, Set Up By Your AI Agent

A copy-paste guide to building a secure Ubuntu 24.04 server where an AI agent (Hermes Agent) does ~80% of the work — driven from a desktop app you install on your own machine first.

The idea

Most VPS guides make you hand-type dozens of commands. This one flips it: you install the agent in minute five, hand it a single detailed build prompt, and it executes and verifies each phase. Your manual part is provisioning, one login URL, and approving the sensitive steps.

Time budget

StepWho does itTime
Provision VPSYou10 min
Install desktop app locallyYou5 min
Install Hermes Agent on the VPSYou5 min
Hardening, firewall, Tailscale, Docker, CaddyAgent15 min (unattended)
Tailscale login click-throughYou2 min
Remote desktop (optional)Agent5 min
Messaging gateway (optional)You10 min

Phase 1 — Provision the server You

  1. Create a Hetzner Cloud account: console.hetzner.cloud
  2. New server, Ubuntu 24.04 LTS. CPX11 (2 vCPU / 4 GB) is plenty to start; go CPX21 if you plan to run containers with real load.
  3. Add your SSH public key at creation timeHetzner SSH key docs. Password-based SSH gets brute-forced within minutes of a box going live, so keys-only from the start.
  4. Note the IPv4 address. Log in: ssh root@YOUR_SERVER_IP

Any provider works

The rest of this guide is provider-agnostic. Hetzner is used because it's cheap and well-supported, but the same steps apply to DigitalOcean, Linode, Vultr, AWS Lightsail, etc. Ubuntu 24.04 LTS is the assumed OS throughout.

Phase 2 — Install the Hermes desktop app You

Do this on your own computer, before touching the VPS further. It's the friendliest way to use Hermes — especially if this is your first time with an AI agent — and it becomes your control panel for the entire rest of the build.

hermes desktop    # after installing Hermes locally (see install.sh below)

If you haven't installed Hermes on your local machine yet, run the same installer you'd use on the server:

curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash
hermes setup      # pick your LLM provider + paste its API key
hermes desktop    # launches the native app (alias: hermes gui)

Native app for macOS, Linux, and Windows. What you get:

Why this order

Your first Hermes experience shouldn't be inside a tmux session over SSH. The desktop app is a client front-end for one or more Hermes agents — including agents running on remote machines. Install it locally first, get comfortable, then register your VPS as a connection and drive the whole server build from it.

One app, many agents

The desktop app manages multiple agent connections side by side — a local one (this machine) and any number of remote ones (your VPS, LAN boxes, etc.), switchable from the Sessions sidebar. They don't share memory, sessions, or skills — that's intentional isolation, so the agent doing work on your laptop never touches your server's state and vice versa. For this guide you'll be talking to the VPS agent from Phase 3 onward.

Phase 3 — Install the agent on the VPS You

This is the last server-side manual step. SSH in and install Hermes Agent — an open-source terminal AI agent that executes shell commands, edits files, and verifies its own work:

ssh root@YOUR_SERVER_IP
apt update && apt full-upgrade -y
curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash
hermes setup     # interactive wizard: pick an LLM provider, paste its API key
hermes doctor    # health check — everything should be green

Docs: hermes-agent.nousresearch.com/docs

Run it inside tmux

Long agent runs must survive SSH disconnects: tmux new -s build, run hermes, detach with Ctrl-B D, reattach later with tmux attach -t build.

Connect your desktop app to it

Once the VPS agent is running, register it in the desktop app you installed in Phase 2: Settings → Gateways → Add connection. The SSH kind is easiest — enter user@host:22 and the app opens the tunnel and starts the remote dashboard for you (no extra server setup). Alternatively, Remote gateway connects over HTTP(S) via LAN, Tailscale, or the internet with a session token. Click Test and wait for "Reachable". From here on, every phase below is driven from your desktop app — you never need to SSH back in except for emergencies. Docs: desktop connections guide.

Phase 4 — Hand the agent the build sheet Agent

Paste this entire prompt into Hermes and let it work. It is deliberately explicit about ordering and safety gates — keep those in if you edit it.

Build prompt — paste into your agent

Harden and set up this fresh Ubuntu 24.04 VPS. Work step by step, verifying each
phase with real command output before moving to the next. Ask before any reboot.

1. Create a sudo user (I'll tell you the username) and copy root's SSH keys
   from /root/.ssh/authorized_keys to the new user.
2. Harden SSH in /etc/ssh/sshd_config.d/99-hardening.conf:
   PermitRootLogin no, PasswordAuthentication no, PubkeyAuthentication yes.
   Validate with `sshd -t`, then open a SECOND verified login before restarting
   ssh. Never restart sshd without that check.
3. Install and enable ufw: default deny incoming, allow outgoing, allow OpenSSH.
   Install and enable fail2ban.
4. Install Tailscale (https://tailscale.com/install.sh), run `tailscale up`,
   and give me the login URL.
5. Install Docker from the official repo
   (https://docs.docker.com/engine/install/ubuntu/), add my user to the docker
   group. CRITICAL RULE: every container port mapping must bind to loopback
   (127.0.0.1:PORT:PORT), never 0.0.0.0. Anything public goes through Caddy.
6. Install Caddy from the official repo (https://caddyserver.com/docs/install).
   Confirm the service is running.
7. Final verification — show me the output of each:
   - `ssh root@localhost` on port 22 should be refused / pubkey-only
   - `ufw status verbose` shows a minimal ruleset
   - `docker ps` format shows no 0.0.0.0 bindings (fine if none running)
   - `tailscale status` connected
   - `systemctl is-active caddy` active
8. Write an AGENTS.md in my home directory documenting the conventions:
   apps live in ~/apps/<appname>/ with docker-compose.yml, public sites get a
   block in /etc/caddy/Caddyfile followed by `systemctl reload caddy`, all
   Docker ports bound to 127.0.0.1, never install nginx, never edit ufw or sshd
   config without asking first.

The one dangerous step

SSH hardening (item 2) is the only step that can lock you out if mis-ordered. That's why the prompt demands sshd -t plus a second working login before restarting sshd — and why you should keep your current session open until the agent confirms both. If it ever skips that sequence, stop it and make it comply. Recovery if it all goes wrong: Hetzner console → your server → Rescue tab gives you an out-of-band root shell.

Why the agent verifies better than you do

A tired human types ufw enable and moves on. The agent pastes back the actual ufw status verbose output, which you can eyeball in ten seconds. Ask it to show verification output at every phase, not to claim "done" — the build prompt above already does this.

Phase 5 — Tailscale: your admin path Agent

The build prompt installs Tailscale. Your part: open the login URL it prints, and sign in (free account covers up to 100 devices: tailscale.com).

Install Tailscale on your laptop/phone too: tailscale.com/download. After that, prefer SSH-ing to the server's tailnet name or tailscale ip -4 address instead of the public IP. The public SSH port stays open as a fallback, but daily admin traffic never touches the open internet.

Phase 6 — Docker & Caddy conventions Agent

Both are installed by the build prompt. The important part is the layout, which the agent writes into AGENTS.md so every future session follows it:

Phase 7 — Remote desktop on the VPS (optional) Agent

A full desktop costs ~1 GB RAM. If you don't genuinely need a GUI, skip this. If you do, tell the agent:

Prompt

Install XFCE and xrdp for remote desktop. Enable xrdp. Do NOT open port 3389
in ufw — RDP must only be reachable over the Tailscale network. Verify xrdp is
listening and confirm ufw rules unchanged.

Then connect from your machine's RDP client to the server's Tailscale IP (tailscale ip -4), never the public IP. RDP exposed to the open internet is one of the most aggressively scanned services on the web.

Phase 8 — Chat with your server from your phone (optional) You

Hermes connects to Discord, Telegram, Slack, and a dozen other platforms, giving the same agent your phone's chat apps:

hermes gateway install

After this you can message the VPS ("check disk usage", "restart the myapp container") from anywhere — which, combined with Tailscale, makes the remote-desktop step unnecessary for most admin work.

Final checklist You

The server isn't "done" when things are running — it's done when these all pass:

Pitfalls & recovery