Deploying hermes as a fly.io "claw"¶
You can deploy hermes as a long-running "claw" on fly.io, reachable over a messaging gateway. These instructions assume Telegram; adapt for other messaging gateways.
Install and authenticate flyctl:
Create fly.toml:
app = "my-hermes-agent-claw"
primary_region = "iad"
[env]
TZ = "America/New_York"
# Signal the entrypoint to skip local defaults and auto-detect providers from API keys.
HARNESS_CLOUD_MODE = "1"
# Persist the faster-whisper model cache across restarts.
# Without this, the model re-downloads (~142 MB) on every deploy.
HF_HOME = "/home/harness/.hermes/.cache/huggingface"
[build]
image = "ghcr.io/boldblackai/harness:hermes-1.9.1"
[processes]
app = "hermes gateway"
# Persistence volumes — mirror what the `harness` CLI bind-mounts on an
# interactive run, so claw state survives restarts: hermes config/sessions,
# XDG config (jj/git config the agent writes), and mise tools & trust
# settings. Each path needs its own fly volume.
[[mounts]]
source = "my_hermes_agent_claw_data"
destination = "/home/harness/.hermes"
initial_size = "1gb"
[[mounts]]
source = "my_hermes_agent_claw_config"
destination = "/home/harness/.config"
initial_size = "1gb"
[[mounts]]
source = "my_hermes_agent_claw_mise_data"
destination = "/home/harness/.local/share/mise"
initial_size = "1gb"
[[mounts]]
source = "my_hermes_agent_claw_mise_state"
destination = "/home/harness/.local/state/mise"
initial_size = "1gb"
[[vm]]
size = "shared-cpu-1x"
memory = "512mb"
[[restart]]
policy = "always"
max_retries = 3
Create the app, volume, and secrets, then deploy:
fly apps create my-hermes-agent-claw
fly volumes create my_hermes_agent_claw_data --region iad --size 1 --app my-hermes-agent-claw
fly volumes create my_hermes_agent_claw_config --region iad --size 1 --app my-hermes-agent-claw
fly volumes create my_hermes_agent_claw_mise_data --region iad --size 1 --app my-hermes-agent-claw
fly volumes create my_hermes_agent_claw_mise_state --region iad --size 1 --app my-hermes-agent-claw
fly secrets set OPENROUTER_API_KEY=<your-key> --app my-hermes-agent-claw
# https://hermes-agent.nousresearch.com/docs/user-guide/messaging/telegram#option-b-manual-configuration
fly secrets set TELEGRAM_BOT_TOKEN=<your-token> --app my-hermes-agent-claw
fly secrets set TELEGRAM_ALLOWED_USERS=<your-user-ids> --app my-hermes-agent-claw
fly deploy --app my-hermes-agent-claw
Message the bot via Telegram, or wire it up to a scheduled workflow — see the daily briefing bot guide for an example.
GitHub authentication (gh CLI)¶
To use gh (or git push / git pull over HTTPS) from inside the claw, authenticate once — the session persists in ~/.config (a mounted volume), so it survives restarts. See GitHub authentication for creating a PAT.
Fly secrets are the cleanest way to get a token into the running machine without it touching any shell history, so use a throwaway secret: set it, log in, then drop it.
# 1. Temporarily expose the PAT to the running machine (triggers a redeploy)
fly secrets set GH_PAT=<your-github-pat> --app my-hermes-agent-claw
# 2. Log `gh` in on the running machine. Credentials land in ~/.config/gh
# (on the persisted volume), so they survive restarts.
fly ssh console --app my-hermes-agent-claw \
-C 'sh -c "echo $GH_PAT | gh auth login --with-token && gh auth status"'
# 3. Drop the PAT — the persisted gh session is all that's needed.
fly secrets unset GH_PAT --app my-hermes-agent-claw
Prefer a quick interactive login?
fly ssh console --app my-hermes-agent-clawopens a shell on the running machine; runecho "<your-pat>" | gh auth login --with-tokenthere.
Customizing the claw — don't extend the image¶
When you want to give the claw extra capabilities (tool wrappers around your APIs, an opinionated initial system prompt, custom gh-style scripts), the temptation is to write a Dockerfile that does FROM ghcr.io/boldblackai/harness:hermes-1.9.1 and bakes everything in. Don't. Two problems:
- The fly volume mounts on top of
/home/harness/.hermes, which silently hides anything youCOPYinto that path on first boot. - Hermes treats
config.yamlas mutable state — TUI tweaks, model switches, and persona toggles are persisted viasave_config(). A derived image fights that ownership.
The supported pattern is to use the upstream image unmodified and inject your customizations via fly's [[files]] section.
Tool wrappers and scripts — use non-volume paths so they're refreshed on every deploy. fly [[files]] preserves the local file's exec bit, so your scripts run as-is from the agent's sandbox.
Config and persona files — the fly volume mounts over /home/harness/.hermes, which hides anything placed there by [[files]]. Instead, seed them by SSH'ing in and copying directly into the volume, or set values with hermes config set. The entrypoint only seeds a minimal config.yaml from a baked-in template on first run in local mode; there is no bulk cp -rn seed mechanism.
Example — add a crm API wrapper script and seed a system prompt:
# fly.toml — append to the example above
# Tool wrappers — written to a non-volume path. Refreshed on every deploy.
[[files]]
guest_path = "/etc/myclaw/bin/crm"
local_path = "bin/crm"
# One-time: seed persona into the volume after first deploy
fly ssh console --app my-hermes-agent-claw \
-C 'cat > /home/harness/.hermes/system-prompt.md <<'"'"'EOF'"'"'
You are a helpful assistant with access to a CRM API.
EOF'
The benefits over a derived image:
- Faster deploys — no rebuild, just pull the upstream image and apply files. Seconds instead of minutes.
- Trivial upstream upgrades — bump one tag in
fly.toml. - No fight with hermes over
config.yamlownership. - One fewer artifact to maintain, sign, and verify.