Skip to main content
Illustration by Annie Ruygt of the Greek god Hermes mid-flight, depicted on a black-figure pottery bowl framed by olive branches Hermes is an AI agent from Nous Research with a built-in learning loop: it watches its own output, notices when it had to improvise to finish a task, and writes that experience back as a reusable skill it can call next time. The skill library grows with use, so Hermes works best when it runs continuously on a persistent host rather than a short-lived sandbox, which is exactly what a Fly Machine with an attached volume gives you. This guide walks you through running Hermes on a Fly Machine, configuring it, and reaching its web dashboard from your laptop. You’ll need flyctl installed, a Fly.io account (free trial works), and an LLM API key (Anthropic, OpenAI, Google Gemini, or OpenRouter for access to 200+ models). You’ll use Nous Research’s official prebuilt image (nousresearch/hermes-agent:latest) so there’s no Dockerfile to maintain and no remote builder to wait on. Fly pulls the image straight from Docker Hub.

Create the app and volume

Hermes keeps all its state in /opt/data inside the container. That includes config, API keys, sessions, skills, and memories. You’ll back that with a Fly volume so it persists across deploys and restarts. Pick an app name (must be globally unique on Fly) and a region close to you, then:
3 GB is comfortable headroom for sessions and the bundled skills directory.

Write fly.toml

Create a directory for the deployment config and drop a fly.toml in it:
The machine_config key points to a JSON Machine config file that defines the containers for a multi-container Machine. Each entry in its containers array follows the ContainerConfig schema from the Machines API. Create that file next to fly.toml (called machine_config.json in our example):
A few notes:
  • No [build.dockerfile]. Fly pulls the image directly. Deploys take seconds, not minutes.
  • Multi-container Machine for s6-overlay. Hermes images published from v2026.5.28 onward use s6-overlay as the in-container supervisor. s6-overlay’s /init calls s6-overlay-suexec, which checks getpid() == 1 before doing anything else and aborts otherwise. Running Hermes as a container in a multi-container Machine (via machine_config.json) gives it its own PID namespace, so /init runs as PID 1.
  • No [[services]] block. The gateway talks outbound to chat platforms, so you don’t need a public port. The dashboard exposes API keys and shouldn’t be public; you’ll reach it through a Fly proxy tunnel below.
  • 4 GB / 2 CPU is the recommended size when browser tools (Playwright/Chromium) are active. If you don’t use browser tools you can drop to shared-cpu-1x and 1–2 GB.

Optional: pass API keys as Fly secrets

This guide enters API keys through hermes setup after the first deploy. If you’d rather manage keys with Fly secrets, set the secrets before you deploy:
Then list each one under the container’s secrets array in machine_config.json. Each entry is { "env_var": "<NAME>" }, where <NAME> must exactly match a secret that already exists on the app:
If a listed secret doesn’t exist, the Machine won’t start. The deploy times out without any error in fly logs. Run fly secrets list -a <your-hermes-app> to check. See MachineSecret for the full entry format.

Deploy

--ha=false keeps it to a single machine; Hermes is stateful and you don’t want two gateway processes writing to the same volume. When the deploy finishes, the machine boots, the entrypoint bootstraps /opt/data (creating .env, config.yaml, SOUL.md, sessions/, skills/, etc.), and hermes gateway run starts. It’ll keep running but it has no API key yet, so it can’t talk to a model. Confirm it’s alive:
You should see the bundled skills sync, then the gateway starting up.

Configure Hermes

SSH into the machine. The hermes binary lives at /opt/hermes/.venv/bin/hermes inside the image, but fly ssh console opens a login shell that resets PATH and won’t find it there. Add a symlink into /usr/local/bin (which is always on PATH) so hermes works as a bare command:
Then open a shell on the machine and run the setup wizard:
The wizard walks you through model selection, tool configuration, and connecting your messaging platforms. When it’s done, exit the SSH session. Restart the machine so the gateway picks up the new config:
Get <machine-id> from fly machine list -a <your-hermes-app>.

Web dashboard

Hermes has a web dashboard on port 9119 for managing sessions, skills, and config. The dashboard reads your API keys, so the upstream guidance is to never expose it on a public port. Tunnel to it instead: In one terminal, start the dashboard inside the machine:
Since you’re using a tunnel via this setup, your dashboard isn’t publicly exposed. But hermes requires you to setup dashboard auth if you’re binding to [::]. Setup auth (or use a shim to proxy from localhost, if that’s your style). In a second terminal, open a Fly proxy from your laptop:
Now visit http://localhost:9119 in your browser. Traffic goes over your authenticated WireGuard tunnel; the dashboard isn’t published to the public internet, though it is reachable from other Machines on your organization’s private network When you’re done, Ctrl+C both commands. The gateway keeps running on the machine.

Upgrading

The image is stateless; your data lives on the volume. To pull the latest Hermes:
The deploy pulls nousresearch/hermes-agent:latest again. The second command re-creates the /usr/local/bin/hermes symlink; it lives on the container’s filesystem, not the data volume, so each new container starts without it.

VM sizing

If you’re running heavy tool use or multiple concurrent sessions, scale up:

Useful commands

Troubleshooting

Gateway won’t start Check hermes doctor for missing API keys or other diagnostics:
Deploy times out with “timeout reached waiting for machine’s state to change” If fly logs stops after “Successfully prepared image” and the Machine never starts, check that every secret in machine_config.json’s secrets array exists:
A missing secret stops the Machine before Hermes runs, so Hermes itself logs nothing. Out of memory Increase RAM:
Need to start fresh Wipe the config files (skills, sessions, and memories survive):
To wipe everything including conversations, destroy and recreate the volume:
Skills behaving unexpectedly List, view, and delete: