Skip to content

Hermes Agent

Open Source AI Agent

Hermes Agent is an open-source AI agent by Nous Research. It runs as a long-lived process with its own conversation history, memory store, scheduled jobs, hooks, and skills โ€” all persisted under $HERMES_HOME (default ~/.hermes).

Official installation reference: hermes-agent.nousresearch.com/docs/getting-started/installation.

Why a Dedicated Container?

  • Stateful โ€” sessions, memories, and skills accumulate over time and must outlive image rebuilds
  • Unprivileged โ€” the agent has shell-execution capabilities, so it should not run as root
  • Versioned โ€” upstream tags follow CalVer (vYYYY.M.D), so image upgrades are frequent

Install

Infrastructure as Code

This Hermes Agent instance is deployed using OpenTofu with a custom Incus image. The infrastructure configuration manages container provisioning and a persistent storage volume for all agent data.

The canonical configuration is maintained at Benoit/OpenTofu.

Custom Hermes Agent Image

The Hermes Agent Incus image is built and maintained at forgejo.benoit.jp.net/Benoit/Laminar.

Image Layout

The Laminar job follows the "Non-sudo system service user" recipe from the official installation docs: it creates an hermes user (UID 1001) and runs the upstream curl โ€ฆ | bash installer as that user with --skip-browser, producing the per-user (git) layout documented upstream:

Path Purpose
/home/hermes/.hermes/hermes-agent/ Git checkout of the upstream repo (code, read-mostly)
/home/hermes/.local/bin/hermes Entrypoint command
/home/hermes/.hermes/.env API keys, model selection, runtime config
/home/hermes/.hermes/sessions/ Conversation history
/home/hermes/.hermes/memories/ Persistent memory store
/home/hermes/.hermes/skills/ User-installed skills
/home/hermes/.hermes/cron/ Scheduled jobs
/home/hermes/.hermes/hooks/ User hooks
/home/hermes/.hermes/logs/ Agent logs
/home/hermes/.hermes/image_cache/ Cached images for vision tools
/home/hermes/.hermes/audio_cache/ Cached audio for TTS
/home/hermes/.hermes.skeleton/ Pristine copy of the above, baked into the image
/home/hermes/.hermes/node/bin/claude Claude Code CLI, bundled via npm install -g @anthropic-ai/claude-code
/home/hermes/hermes-webui/ hermes-webui web UI (code, image rootfs)
/home/hermes/.hermes/webui/ hermes-webui state โ€” sessions, settings (persistent volume)

The Playwright/Chromium browser stack is not installed in the image (--skip-browser). If you need the agent's browser tools, install it on the running container after first boot โ€” see Enable Browser Tools (Optional) below.

Infrastructure Configuration

The OpenTofu configuration provisions:

  • Incus instance running the custom Hermes Agent image
  • One persistent storage volume mounted at /home/hermes/.hermes for all agent state (sessions, memories, skills, config, logs)
OpenTofu Configuration
resource "incus_storage_volume" "hermes_home" {
  name = "hermes_home"
  pool = incus_storage_pool.default.name
}

resource "incus_instance" "hermes" {
  name  = "hermes"
  image = "laminar.incus:hermes-agent-2026.5.16-1benoitjpnet"

  device {
    name = "hermes_home"
    type = "disk"
    properties = {
      path   = "/home/hermes/.hermes"
      source = incus_storage_volume.hermes_home.name
      pool   = incus_storage_pool.default.name
    }
  }
}

Deploy Infrastructure

Apply OpenTofu configuration
tofu apply

First-Boot Bootstrap

Empty Volume Shadows Skeleton

A freshly-provisioned Incus volume mounts on top of /home/hermes/.hermes and hides the skeleton (.env, empty sessions/, memories/, etc.) that the image ships with. Seed it once from the pristine copy at /home/hermes/.hermes.skeleton/.

Enter the container
incus shell hermes
Seed the volume (run once, as root inside the container)
if [ ! -f /home/hermes/.hermes/.env ]; then
  cp -a /home/hermes/.hermes.skeleton/. /home/hermes/.hermes/
  chown -R hermes:hermes /home/hermes/.hermes
fi
Run the interactive setup wizard (as the hermes user)
su - hermes -c 'hermes setup'

The wizard writes API keys and model preferences into /home/hermes/.hermes/.env โ€” i.e. onto the persistent volume, so it survives image upgrades.

During the wizard you'll be asked for:

  • Anthropic / Claude token โ€” paste an API key from console.anthropic.com when prompted. The wizard stores it in /home/hermes/.hermes/.env.

Authenticate Claude Code (Optional)

Claude Code is pre-installed (see the Image Layout table) but ships unauthenticated. Authenticate as the hermes user:

Generate a long-lived Claude Code token
incus exec hermes --user 1001 --env HOME=/home/hermes -- bash -c '
  export PATH=/home/hermes/.hermes/node/bin:$PATH
  claude setup-token
'

claude setup-token walks you through OAuth and prints a token suitable for non-interactive use. Paste it into the Hermes setup wizard when prompted for the Anthropic / Claude credential, or export it as ANTHROPIC_API_KEY in /home/hermes/.hermes/.env. Credentials Claude Code stores for itself live under /home/hermes/.claude/ โ€” outside the .hermes volume, so symlink that directory into .hermes or attach a second volume if you want it to survive image rebuilds.

hermes-webui

hermes-webui is pre-installed at /home/hermes/hermes-webui and starts automatically via systemd on every boot. On first start it sets up its Python venv and is then available at http://<container-ip>:8787.

Check the service status:

incus exec hermes -- systemctl status hermes-webui.service
incus exec hermes -- journalctl -u hermes-webui.service -f

To enable password protection, add to /home/hermes/.hermes/.env:

HERMES_WEBUI_PASSWORD=your-strong-password

Then restart the service:

incus exec hermes -- systemctl restart hermes-webui.service

Enable Browser Tools (Optional)

The image is built with --skip-browser, so Playwright and Chromium are not present. To enable the agent's browser tools, run these on the live container after first boot:

System libs Chromium needs (apt) โ€” run as root inside the container
incus exec hermes -- bash -c '
  export PATH=/home/hermes/.hermes/node/bin:$PATH
  cd /home/hermes/.hermes/hermes-agent
  npx playwright install-deps chromium
'
Chromium binary โ€” run as the hermes user
incus exec hermes --user 1001 --env HOME=/home/hermes -- bash -c '
  export PATH=/home/hermes/.hermes/node/bin:$PATH
  cd /home/hermes/.hermes/hermes-agent
  npx playwright install chromium
'

The Chromium binary and Playwright cache land in /home/hermes/.cache/ms-playwright/, which sits outside the /home/hermes/.hermes volume โ€” so this step has to be re-run after each image upgrade. If you want it persisted, mount a second volume at /home/hermes/.cache as well.

Upgrade

Incus Image Upgrade

Image upgrades replace /home/hermes/.hermes/hermes-agent/ (the code), but the persistent volume keeps every byte of agent state (sessions, memories, skills, config) untouched. No backup/restore step is required.

The persistent volume already holds everything stateful. No manual backup is needed before upgrade.

:material-snapshot: Optional snapshot

For peace of mind, snapshot the volume before the upgrade:

incus storage volume snapshot default hermes_home "pre-upgrade-$(date +%Y%m%d)"

Update OpenTofu configuration

Edit your OpenTofu configuration file to update the image version:

resource "incus_instance" "hermes" {
  name  = "hermes"
  image = "laminar.incus:hermes-agent-NEW-VERSION-1benoitjpnet"  # Update this line
  # ...
}

Apply infrastructure changes

Re-provision Incus container on the new image
tofu apply

Verify

incus exec hermes -- su - hermes -c 'hermes --version'

Upgrade Complete

Your Hermes Agent instance is now running on the new Incus image with all sessions, memories, and skills intact.

Building a New Image Version

Bump the version in the Laminar job and queue it:

cfg/jobs/hermes-agent.run
UPSTREAM_VER="2026.5.16"   # update to the desired upstream tag (without leading v)
Queue the build
laminarc queue hermes-agent

The job clones https://github.com/NousResearch/hermes-agent at tag v$UPSTREAM_VER, installs as the hermes user, and publishes the image as hermes-agent-$UPSTREAM_VER-1benoitjpnet.


Related Documentation: