Skip to main content

Self-host the Hermes Agent with persistent memory

· 14 min read
Rafael Fernandes
NLP Engineer & Tech Writer at WiLine
Share:
Hermes Agent+SQLite

Hermes Agent is Nous Research's open-source (MIT) AI agent — "the agent that grows with you." Its standout feature is persistent memory: it learns your projects and doesn't forget across restarts. This guide deploys it on the same WEC Instance you already use for OpenClaw, points it at a model, and proves the memory survives a full reboot.

Reproducibility

Runs on the same WEC Instance from the OpenClaw series — no new VM. The installer pulls its own Python (via uv), Node.js browser tools, and a Playwright Chromium automatically. State lives in ~/.hermes/. The messaging gateway runs as a systemd user service, so it coexists with the OpenClaw Docker stack. Captured live on Ubuntu 22.04 with hermes v0.17.0.

Why persistent memory matters

An agent that forgets on every restart is a toy. Hermes keeps a durable store on disk under ~/.hermes/:

  • ~/.hermes/memories/MEMORY.md — a human-readable file of facts the agent has chosen to remember.
  • ~/.hermes/state.db — a SQLite database holding every session and message, with FTS5 full-text search indexes layered on top so the agent can search its own history.

A deploy, crash, or reboot doesn't wipe what it learned. That's the difference between a chat demo and an agent you can rely on day to day — and it's exactly what we test at the end.

The moving parts

Everything runs on one WEC Instance. Hermes talks to a model over the WEC Inference API, keeps its memory on local disk, and runs as a systemd user service — so it restarts itself after a reboot and its memory is still there:

Prerequisites

  • An existing WEC Instance with shell access (the OpenClaw box works).
  • A model: either a Nous Portal login, or your own provider API key (we use an OpenAI key here; WEC Models with glm-5.2 works too).
  • Nothing to pre-install — the installer handles Python, Node.js, and the browser engine.

Step 1 — Install Hermes

curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash

Running the install one-liner — the Hermes installer starts resolving and downloading packages

The installer resolves a Python virtual environment with uv, installs the Node.js browser tools, downloads a Playwright Chromium, syncs the bundled skills, and drops a hermes launcher into ~/.local/bin:

Output
✓ Main package installed (hash-verified via uv.lock)
✓ All dependencies installed
✓ Browser engine setup complete
✓ Installed hermes launcher → ~/.local/bin/hermes
✓ Configuration directory ready: ~/.hermes/
→ Starting setup wizard...

It then launches the setup wizard automatically. Reload your shell afterward so the hermes command is on your PATH:

source ~/.bashrc

Step 2 — Run the setup wizard

The wizard walks through setup type, terminal backend, and messaging. On a box that already ran OpenClaw, it offers to import the old config first — you can preview what would be migrated before anything changes.

The first choice is how to set up Hermes:

  • Quick Setup (Nous Portal) — one OAuth login, no API keys. The Portal bundles a model and a tool gateway. Requires an active Nous Portal subscription.
  • Full setup (bring your own keys) — you configure providers and paste your own keys.

The "How would you like to set up Hermes?" prompt with Quick Setup, Full setup, and Blank Slate options

What we actually did

We chose Quick Setup first. The Portal login showed a device code and sat at "Waiting for approval (polling)" — it needs an active Portal subscription, which this account didn't have. We pressed Ctrl+C to cancel and instead brought our own model in Step 3 (below) with an OpenAI key. If you do have a Portal plan, complete the login here and you can skip Step 3.

For the rest of the wizard:

  • Terminal backend: Local — commands run directly on this machine.
  • Messaging: Skip for now (you can wire up Telegram/Discord later with hermes setup gateway).

The wizard applies sensible agent defaults (max iterations, a 50% context-compression threshold) and finishes with a tool-availability summary.

Step 3 — Connect a model

Out of the box the default model routes through the Nous Portal / OpenRouter and needs a key. To use your own provider instead, run:

hermes setup model

Then select OpenAI ▸ OpenAI API, paste your API key when prompted (it's written to ~/.hermes/.env — never echoed), accept the default base URL https://api.openai.com/v1, and pick a model — we used gpt-5.5:

Output
API key saved.
Base URL [https://api.openai.com/v1]:
Select default model:
→ (●) gpt-5.5

Choosing the default model in hermes setup model — gpt-5.5 selected from the OpenAI list

Use WEC Models instead

To run inference on WiLine's own endpoint, choose Custom endpoint, set the base URL to https://inference.wiline.com/v1, paste your WEC API key, and use a model like glm-5.2. Same OpenAI-compatible flow.

Confirm the model and key are healthy:

hermes doctor
Output
◆ Configuration Files
✓ ~/.hermes/.env file exists
✓ API key or custom endpoint configured
...
◆ Memory Provider
✓ Built-in memory active (no external provider configured — this is fine)

The remaining lines in doctor are optional tool integrations (web search, Discord, image gen) — not needed for this guide.

Step 4 — Chat and save a memory

Start an interactive session:

hermes

The Hermes Agent TUI welcome screen — ASCII logo, available tools and skills, model gpt-5.5, and the chat prompt

Send it a fact and ask it to remember — watch for the 🧠 memory tool to fire:

In the Hermes TUI
● Please save to your long-term memory: my WEC project codename is
Bluefin-7, running on the OpenClaw box.
┊ 🧠 memory ? 0.0s

╭─ ⚕ Hermes ─────────────────────────────────────────────────────────╮
Saved to long-term memory: your WEC project codename is Bluefin-7,
running on the OpenClaw box.
╰─────────────────────────────────────────────────────────────────────╯

Hermes firing the memory tool and confirming the fact was saved to long-term memory

That write creates ~/.hermes/memories/MEMORY.md. Exit with /exit or Ctrl+D.

Paste gotcha

The TUI submits on newline, so pasting multi-line text can send a half-typed message. If a prompt only catches a fragment, type it on one line by hand.

Step 5 — Prove memory survives a reboot

This is the whole point. The Hermes gateway is installed as a systemd user service with linger enabled, so it comes back automatically after a reboot — no login required. Restart the service, then reboot the whole box to make the test airtight:

systemctl --user restart hermes-gateway # restart just the gateway service
sudo reboot # then reboot the entire box

Restarting the gateway and rebooting the box — the SSH connection closes as the host goes down

Reconnect once the box is up and confirm the service restarted on its own:

systemctl --user status hermes-gateway --no-pager
Output
● hermes-gateway.service - Hermes Agent Gateway - Messaging Platform Integration
Loaded: loaded (...; enabled; vendor preset: enabled)
Active: active (running) since Thu 2026-06-25 21:21:54 UTC; 1min 36s ago

systemctl status showing hermes-gateway active and running since boot, started automatically by linger after the reboot

Now open a fresh session and ask — no prior context, brand-new session ID:

hermes
In the Hermes TUI
● What's my WEC project codename, and which box does it run on?

╭─ ⚕ Hermes ─────────────────────────────────────────────────────────╮
Your WEC project codename is Bluefin-7, and it runs on the
OpenClaw box.
╰─────────────────────────────────────────────────────────────────────╯

A fresh Hermes session after reboot correctly recalling the Bluefin-7 codename from disk

It recalled the fact from disk after a full reboot. Inspect where that durable state lives:

cat ~/.hermes/memories/MEMORY.md
file ~/.hermes/state.db && du -h ~/.hermes/state.db
Output
User's WEC project codename is Bluefin-7, running on the OpenClaw box.

/home/ubuntu/.hermes/state.db: SQLite 3.x database, ...
176K /home/ubuntu/.hermes/state.db

A plain-text MEMORY.md you can read and edit, backed by a SQLite state.db for sessions. That's the persistent memory — and it just outlived a reboot.

How the memory is stored — two files, two jobs

When you ask Hermes to remember something, it writes the fact to ~/.hermes/memories/MEMORY.md — plain Markdown you can open, read, and even edit by hand. Separately, your full conversation history is kept in ~/.hermes/state.db, a SQLite database. Both live on disk, which is why a reboot doesn't lose them.

The simplest way to see what the agent knows is just to read the Markdown:

cat ~/.hermes/memories/MEMORY.md

To look inside the database, note that SQLite's engine is built into Python, so Hermes uses it without any extra install. The optional sqlite3 command-line tool — for browsing the DB by hand — isn't installed by default. If you want it:

sudo apt install -y sqlite3
sqlite3 ~/.hermes/state.db '.tables'

Output of .tables on state.db — sessions and messages tables alongside the messages_fts FTS5 family

You'll see sessions and messages tables holding the conversation, plus a family of messages_fts* tables. Those are SQLite FTS5 full-text indexes — including a trigram variant for fuzzy matching — that let Hermes search its own past conversations:

Output (from .schema)
CREATE VIRTUAL TABLE messages_fts USING fts5(content);
CREATE VIRTUAL TABLE messages_fts_trigram USING fts5(content, tokenize='trigram');

And each session is a row you can inspect — here are the two from this guide, the second one created after the reboot:

sqlite3 -header -column ~/.hermes/state.db \
"SELECT substr(id,1,15) AS id, model, message_count FROM sessions ORDER BY started_at;"
Output
id model message_count
--------------- ------- -------------
20260625_211227 gpt-5.5 8
20260625_212333 gpt-5.5 2

You never need the CLI for Hermes to work — it's only for inspecting the database yourself. Day to day, the human-readable MEMORY.md is all you have to look at.

Bonus — read the conversation the agent stored

The messages table holds the actual text of every turn. Each row has a roleuser (what you typed), assistant (Hermes's reply), or tool (a tool's result) — so you can replay the whole exchange:

sqlite3 -header -column ~/.hermes/state.db \
"SELECT substr(session_id,10,6) AS sess, role, substr(content,1,55) AS content
FROM messages ORDER BY timestamp;"
Output
sess role content
------ --------- -------------------------------------------------------
211227 user hello, can you hear me?
211227 assistant Yes — I can hear you. How can I help?
211227 user Please save to your long-term memory: my WEC project...
211227 tool {"success": true, "done": true, "target": "memory", ...
211227 assistant Saved to long-term memory: your WEC project codename...
212333 user What's my WEC project codename, and which box does...
212333 assistant Your WEC project codename is Bluefin-7, and it runs...

The messages table in state.db showing the full conversation — user, assistant, and tool rows across both sessions

The interesting part is how the memory got written. The assistant didn't edit a file directly — it emitted a memory tool call, and that tool is what wrote MEMORY.md. You can see the exact call recorded in the history:

sqlite3 ~/.hermes/state.db \
"SELECT tool_calls FROM messages WHERE tool_calls LIKE '%memory%' LIMIT 1;"
Output
{"function": {"name": "memory", "arguments":
"{\"target\":\"memory\",\"operations\":[
{\"action\":\"add\",
\"content\":\"User's WEC project codename is Bluefin-7, running on the OpenClaw box.\"}]}"}}

The recorded memory tool call in the messages table — the add operation that wrote the Bluefin-7 fact to MEMORY.md

So the full chain is: you type → the agent decides it's worth keeping → it calls the memory tool with action: add → that writes the fact into MEMORY.md, while the whole conversation stays in state.db, full-text searchable via FTS5. Two stores, one durable memory.

What can you do with it?

A memory that survives reboots only matters because of what the agent does with it. With Hermes running, the same install ships these capabilities (you saw them in the hermes doctor tool list) — here's what they unlock day to day:

  • Run real work on the box. The terminal and code_execution tools let it act, not just chat — e.g. "clone this repo, install deps, and run the tests," or "check why the disk is filling up." It does it on the WEC Instance and reports back.
  • Drive it from your phone. A messaging gateway (Telegram, Discord, Slack, and more) means you can hand it a task from anywhere — no SSH. (Setup in Part 2.)
  • Put it on a schedule. The cronjob tool runs recurring jobs: a morning briefing, a weekly disk-usage report, or watch a service and alert you when it fails — delivered to your messaging channel. (Part 3.)
  • Browse and search the web. With the browser and web-search tools it can pull live information and act on real pages. (These need provider API keys — see hermes setup tools.)
  • Grow reusable skills and delegate. It can build skills from what it learns and hand subtasks to subagents — the "agent that grows with you" part.
  • Remember across all of it. Everything above is backed by the persistent memory you just set up, so context carries from one task — and one day — to the next.

The rest of this series turns each of these into a hands-on guide (see What's next).

Troubleshooting (real)

  • Quick Setup (Nous Portal) hangs at "Waiting for approval (polling)". The Portal login needs an active subscription/credit. If you don't have one, Ctrl+C and use Full setup with your own key instead (Step 3).

  • doctor shows "No API key found in ~/.hermes/.env". The default model points at the Portal/OpenRouter. Run hermes setup model, pick your provider, and paste the key — doctor then reports "API key or custom endpoint configured."

  • Deprecated .env warning on gateway start (e.g. MESSAGING_CWD). Harmless; follow the hint to move the setting into config.yaml when convenient.

  • Repeating WARNING [Telegram] … in the gateway logs. If you followed the earlier OpenClaw parts and added a Telegram channel, Hermes's setup detected OpenClaw and imported those messaging settings into ~/.hermes/.env. Because we skipped finishing messaging here, the gateway has a token but no complete config, so it keeps retrying and logging this warning. (A fresh box with no OpenClaw Telegram setup won't see it.) Remove the stray entries and restart:

    sed -i '/^TELEGRAM_ALLOWED_USERS=/d; /^TELEGRAM_BOT_TOKEN=/d' ~/.hermes/.env
    systemctl --user restart hermes-gateway

    If that token was ever exposed, revoke it in Telegram's @BotFather and issue a new one.

What's next

Now that Hermes is running with durable memory, the rest of the series turns each capability above into a hands-on guide:

  • Part 2 — Drive Hermes from Telegram. Finish migrating the Telegram channel Hermes already imported from OpenClaw (hermes setup gateway, allowlists, verify the bot responds), so you can hand it tasks from your phone.
  • Part 3 — Put it on a schedule. Use the cron tool for a morning briefing, a weekly report, and a service-health alert delivered to your channel.
  • Part 4 — Custom skills & subagents. Teach it reusable skills and delegate work.

Links will appear here as each part ships.