How I run AI agents on a Hetzner VPS
I keep my always-on AI agents on one Hetzner Linux server. This is the version of the setup I can publish, from an empty server to isolated agents, Telegram control, scheduled jobs, monitoring and recovery.

I use a Hetzner VPS because an agent on my laptop needs the lid open, the network connected and the right session still running. A small Linux server removes all three dependencies. The model stays the same; the server adds a permanent address, a clock, a filesystem and a process that can restart after a failure.
This guide describes the architecture I use. It leaves out private IP addresses, credentials, internal paths, customer data and anything that would map the open ports and services of a live machine. The same method works with other reputable VPS providers; Hetzner is where my always-on runtime currently lives.
What the VPS does
A model produces a response. An agent runtime wraps that model in memory, tools, files, permissions, schedules and delivery channels. The VPS hosts that layer twenty-four hours a day.
One machine holds six layers with separate jobs:
- Linux hostUsers, SSH, firewall, patches, disk and network.
- RuntimeHermes, another agent framework or your own service.
- ProfilesSeparate identity, model, memory, permissions and files.
- ChannelsTelegram, a terminal, an API or another private control channel.
- SchedulesMonitors, summaries, refreshes and maintenance windows.
- RecordsLogs, output files, health checks and proof of delivery.
Create the Hetzner server
In Hetzner Console, create a project and add a server. Hetzner’s current server-creation guide shows the interface as it is today. These are the choices that matter:
- Location: choose a region near the services and people that will use the agent. Location affects latency and where your data is handled, so choose it on purpose.
- Image: use a supported Linux long-term-support release. An end-of-life image starts you with maintenance debt on day one.
- Size: start with enough memory for the runtime, containers and expected concurrency. Agents that call a hosted model API usually need a server that stays up and keeps its state more than they need large local-model compute.
- Networking: a public IP is convenient for SSH, but expose only the traffic you need. Hetzner also supports servers with no public IP on a private network, for designs that do not need direct public access.
- SSH key: add your public key during creation. If key authentication is available, do not start with a reusable root password.
- Firewall and backups: attach both at creation instead of promising to return to them later.
Hetzner documents configurable firewalls, private networks, automatic backups, snapshots, cloud-init and rescaling. Keep the first build small enough that you can explain every open port and running process. Hetzner supplies the virtual machine; the operating system and what it exposes are yours to run.
Secure the first login before installing the agent
Connect with the key you attached, using ssh root@YOUR_SERVER_IP. Update the base system, then create a non-root operator account:
apt update
apt upgrade
adduser agentops
usermod -aG sudo agentops
install -d -m 700 -o agentops -g agentops /home/agentops/.ssh
cp /root/.ssh/authorized_keys /home/agentops/.ssh/authorized_keys
chown agentops:agentops /home/agentops/.ssh/authorized_keys
chmod 600 /home/agentops/.ssh/authorized_keys
Open a second terminal and check that ssh agentops@YOUR_SERVER_IP works before changing SSH policy. Keep the original session open until the new login works. Then review the SSH configuration, disable password authentication, validate with sshd -t and reload SSH. Read any hardening snippet before you paste it. Cloud images can put settings in included files, and a change that locks you out still reports success.
At the Hetzner firewall, allow inbound SSH only from trusted source addresses where practical. Open ports 80 and 443 only if you mean to host a web endpoint. A Telegram bot makes outbound connections, so it needs no random public “agent port”. Allow outbound traffic only as broadly as the model, package, messaging, time and update services require.
Install the runtime, then prove one local turn
There are two sensible installation shapes, plus a third for scaling up:
- Simple · one agent, one user
- Install the agent runtime under the limited Linux account with its supported service installer. This shape is the easiest to understand.
- Isolated · container plus volumes
- Run the runtime in Docker, store profiles and workspaces in declared volumes, and keep the container replaceable.
- Scaled · several supervised profiles
- Give each agent its own profile, token, model policy, working directory and restart boundary.
For a Docker-based build, follow Docker’s official Ubuntu repository instructions instead of an old blog post. Check both the engine and Compose before adding an agent:
docker version
docker compose version
sudo systemctl is-enabled docker
sudo systemctl is-active docker
Docker’s own documentation warns that published container ports can bypass rules created with ufw or firewalld. Keep the Hetzner network firewall as the outer boundary, publish no port by default, and read Docker’s packet-filtering guidance before making a container public.
For Hermes, the full public setup is in the Hermes Agent on Telegram setup guide. Install it from the current official instructions, choose a model provider, and complete one harmless terminal request before adding Telegram or schedules. If a basic local turn cannot authenticate and return, more infrastructure will only hide the original failure.
Add a model and Telegram only after the terminal path works
The model provider does the reasoning, Hermes is the agent runtime and Telegram is the control channel. With those roles kept apart, failures are easier to trace and models are easier to replace.
- Create a dedicated bot with Telegram’s official @BotFather.
- Store the bot token and model-provider key in the runtime’s private configuration. Keep them out of shell history, repositories, screenshots and Telegram messages.
- Run
hermes gateway setup, configure a strict user allowlist, and choose a limited working directory. - Start the gateway in the foreground and verify
/whoamiand/statusfrom the allowed Telegram account. - Only then install the supported gateway service, and reboot once to check that it comes back without an open SSH session.
The detailed commands and troubleshooting table are in the Telegram section of the Hermes guide. The order I follow is a local model turn, the foreground gateway, an allowlisted Telegram turn, the persistent service and the reboot test.
Run more than one agent with separate permissions
A second agent needs more than a new bot name. Give it its own identity and limits:
- Profile: separate configuration, memory, session history and model choice.
- Messaging token: one bot token and allowlist per control channel.
- Filesystem: mount or expose only the project directories it needs.
- Service: a restartable process with an unambiguous status command.
- Budget: provider limits sized to that workload.
- Schedule: separate job identity, output destination, timeout and overlap rule.
- Logs: records that show which profile acted, what it observed and where the result went.
Containers help with packaging and filesystem separation, but they do not make the agent secure on their own. What the agent can reach is still decided by the host user, mounted sockets, secrets, network rules and writable volumes.
What agents are good at on a VPS
- Monitor · watch sources
- Fetch known sources, keep each observation, and interrupt only when a threshold changes.
- Schedule · run on time
- Produce digests, maintenance checks or refreshes without depending on a laptop session.
- Route · receive remotely
- Use Telegram as a private place to start, steer, approve and receive scoped work.
- Build · prepare outputs
- Generate reports, files, previews or patches inside a scoped workspace.
- Verify · close on a checked result
- Test the user outcome, keep the logs, and tell a confirmed delivery apart from an attempted one.
The bad fit is unlimited autonomous administration. Do not give a chat-controlled agent unrestricted root access, every project, the Docker socket, all secrets and permission to publish. I let agents observe and prepare work broadly, then narrow their authority as the consequences rise.
How my Hetzner setup is arranged
My live setup is one migrated Hetzner Linux host running Hermes in a container. Persistent profiles survive container replacement. Each Telegram agent has its own profile, token, model route, sessions, service identity and working boundary. A supervisor keeps the gateways running and lets me restart one profile without taking down the others.
The VPS also runs scheduled operational jobs and monitored project runtimes. Each job finds its project from a stable root variable or a path relative to the repository, and never assumes my Mac’s directory layout exists on Linux. Source changes still go through version control, which holds the canonical source tree. For which processes are running and what state they are in, the live VPS is the authority.
| Machine | What it is good for | What it should not be used as |
|---|---|---|
| Phone | Direction, status, approvals, short inputs and receiving finished files. | The machine that owns source files or long-running processes. |
| Mac | Interactive building, visual QA, local repositories, browser state and high-context work. | An always-on production host. |
| Hetzner VPS | Persistent agents, gateways, schedules, monitored services and repeatable Linux execution. | A backup of every source, or a place to act without limits. |
That split gives me two remote-control patterns. Codex on my phone keeps execution on the Mac when the work depends on local source and browser state. Hermes on Hetzner keeps execution on the VPS when the work must stay available after the laptop sleeps.
Operate, back up and recover the server
- Patch on a schedule: update the OS and runtime, read what will change first, and keep a rollback path.
- Watch headroom: monitor memory, disk, inodes, load and container growth. An agent can fill a disk with logs while still replying “healthy”.
- Prove delivery: a job process that exits cleanly does not show that Telegram, email, storage or another downstream system received the result.
- Back up off the live disk: Hetzner automatic backups and snapshots help recover a server, but critical configuration and source also need an independent, tested recovery path.
- Rotate secrets: keep an inventory of credential names and owners, never print values during audits, and rehearse replacement before an incident.
- Test restarts: reboot the host, confirm the expected services return, and test as the same limited runtime identity that runs them.
- Record live state: write down which runtime, profile, service and project path currently own each operational outcome.
Hetzner currently documents daily automatic server-disk backups with seven backup slots, plus snapshots you trigger yourself. Treat both as layers; neither is a full recovery plan on its own. A backup earns trust once you have restored it into an isolated environment and checked the agent, configuration, data and delivery path.
Launch checklist
SERVER
[ ] Supported Linux image
[ ] SSH key attached
[ ] Firewall attached
[ ] Backups enabled
[ ] Limited operator user verified
[ ] Password login disabled
RUNTIME
[ ] Docker or native runtime verified
[ ] One harmless local model turn passes
[ ] Secrets stored outside source control
[ ] Working directory deliberately scoped
[ ] Persistent profile path documented
REMOTE CONTROL
[ ] Dedicated Telegram bot
[ ] Strict user allowlist
[ ] /whoami and /status verified
[ ] Service survives logout and reboot
OPERATIONS
[ ] One profile per agent
[ ] Schedules have timeout and overlap rules
[ ] Logs identify observation, action, and delivery
[ ] Disk, memory, and service health monitored
[ ] Recovery tested from an independent copy
[ ] Consequential actions still require approval
Sources

Want this inside your team’s workflow? The Verified AI Operations Sprint tests one workflow in ten business days. See the sprint →