Running Hermes agents in Docker, free, local and paid
A full walkthrough of boxing Hermes Agent with Docker bind mounts, then wiring up a free local model, free cloud models and a paid ChatGPT-backed coding agent.
The video below walks through three separate Hermes agents running on one personal laptop: a fully local one on a free model, a free one split across a few cloud providers, and a paid one backed by a ChatGPT subscription. This guide is the written version of that setup.
Every file you need, the compose file, the env template and each agent's soul.md, is in the companion repository: github.com/stackagen/docker-hermes-agent-setup. Clone it and you already have the exact folder structure below. This guide explains what each piece does in simple terms; the repo's own README covers plain folder placement, and the video covers the reasoning and the live demo.
Why put an agent in a box first
You can run Hermes Agent directly on your own machine, nothing stops you. The reason to run it in Docker instead is blast radius. An agent with filesystem access is only as safe as the folders you hand it, and a personal laptop usually has folders you would rather it never touch.
Docker's bind mount is the tool for this. A bind mount maps one folder on your real machine to one folder inside the container, and only the folders you explicitly mount are visible inside. Say your laptop has three folders: hermes, vault and bank-details. Mount hermes and vault into the container and leave bank-details out entirely. The agent running in that container has no path to bank-details, because that path simply does not exist from its side. It is not a permission you revoke later, it is a door that was never built. Only mount what you are actually comfortable an agent seeing, never a whole drive.
This setup used an RTX 3070 with 8GB of VRAM, 16GB of DDR5 RAM and a 12th gen i7-12700H. Not top-end hardware, but enough to run everything below.
What you need before you start
| Software | Required | Why |
|---|---|---|
| Docker Desktop | Yes | Must be running before any docker command works |
| Ollama | Yes, for local models | Serves the free local model, and needs "expose to the network" turned on |
| Hermes Desktop | Optional, recommended | A proper UI instead of the raw dashboard |
| Obsidian | Optional | Lets you read the agent-filed notes in a real vault, not just as files |
Getting the folder structure
The fastest way to get every path right is to clone the companion repo instead of building the folders by hand:
git clone https://github.com/stackagen/docker-hermes-agent-setup.gitThat folder is now your Hermes root, laid out like this:
docker-hermes-agent-setup/
├── .hermes/
│ ├── .env your folder paths, read automatically by Docker Compose
│ └── compose.yaml the container definition
├── obsidian/
│ ├── brain_dump/ empty vault, Eevie files notes in here
│ └── pokedex/ empty vault, Pokedex writes research in here
├── coding_space/ empty, Coder Bro drops projects in here
└── soul_md_files/
├── coder_bro_soul.md
├── eevie_soul.md
└── pokedex_soul.mdThe empty folders only hold a .gitkeep placeholder, that is normal, git does not track empty folders. Rename the top folder to anything you like, the name does not matter, only the paths inside .env do.
Wiring the compose file and the env file
Open .hermes/.env. It holds four paths, and Docker Compose reads this file automatically because it sits next to compose.yaml:
# Use forward slashes even on Windows, Docker Desktop parses these more
# reliably than backslashes when they're mixed with the "C:" drive letter.
HERMES_DATA_DIR=D:/Hermes/.hermes
BRAIN_DUMP_OBSIDIAN_VAULT_DIR=D:/Hermes/obsidian/brain_dump
POKEDEX_OBSIDIAN_VAULT_DIR=D:/Hermes/obsidian/pokedex
CODER_BRO_CODING_SPACE_DIR=D:/Hermes/coding_spaceChange every path to wherever you actually cloned the repo, not the example above. compose.yaml reads these same four variable names, so if you rename one here, rename it there too, or the container will not find the right folder:
services:
# One-time (or occasional) setup wizard. Not started by `docker compose up`,
# it only runs when you explicitly ask for it:
# docker compose run --rm setup
setup:
image: nousresearch/hermes-agent:latest
container_name: hermes-setup
profiles: ["setup"]
stdin_open: true
tty: true
volumes:
- ${HERMES_DATA_DIR}:/opt/data
command: setup
# The persistent gateway. This is the one that stays running day to day:
# docker compose pull upgrade
# docker compose up -d start
# docker compose logs -f tail the logs
# docker compose down stop
hermes:
image: nousresearch/hermes-agent:latest
container_name: hermes
restart: unless-stopped
command: gateway run
ports:
- "8642:8642" # Hermes's own OpenAI-compatible API / health endpoint
- "9119:9119" # the web dashboard
volumes:
- ${HERMES_DATA_DIR}:/opt/data
- ${BRAIN_DUMP_OBSIDIAN_VAULT_DIR}:/opt/data/vault/brain_dump
- ${POKEDEX_OBSIDIAN_VAULT_DIR}:/opt/data/vault/pokedex
- ${CODER_BRO_CODING_SPACE_DIR}:/opt/data/coder_bro_data/coding_space
environment:
- OBSIDIAN_VAULT_PATH=/opt/data/vault
- HERMES_DASHBOARD=1
- HERMES_DASHBOARD_INSECURE=1
- HERMES_DASHBOARD_BASIC_AUTH_USERNAME=changeme_username
- HERMES_DASHBOARD_BASIC_AUTH_PASSWORD=changeme_password
deploy:
resources:
limits:
memory: 4G
cpus: "2.0"Change changeme_username and changeme_password directly in this file, not in .env, those two lines are what you will type into the dashboard and the desktop app to sign in. Do not run any docker compose command from anywhere except the .hermes folder, that is the one folder containing compose.yaml, running it from elsewhere is the single most common setup mistake.
Two ports matter here. 8642 is Hermes's own API and health endpoint, not the model. 9119 is the dashboard your browser and desktop app both talk to. Ollama itself is not in this compose file at all, it runs natively on your machine, and the container reaches it at http://host.docker.internal:11434/v1, Docker Desktop wires that address up for you automatically.
Pulling a local model
Hermes recommends a 12 billion parameter model, but that is a preference, not a requirement. On the hardware above, a 9B model such as Qwen3.5 runs comfortably; anything bigger starts fighting the 8GB of VRAM for room. Pull whichever model you settle on:
ollama pull qwen3.5:9bThen open Ollama's settings and turn on "expose Ollama to the network", and set the context length to 64K. Without that toggle, the container cannot reach the model running on your host.
Running the setup wizard
With Docker Desktop and Ollama both running, open a terminal inside the .hermes folder specifically, and run:
docker compose run --rm setupThis launches Hermes's own setup wizard, where you register model providers:
- Free cloud options: NVIDIA NIM and Google AI Studio both hand out free API keys. OpenRouter and Ollama Cloud are worth logging into as well, more providers means more fallback options later.
- Your local model: choose the custom endpoint option, and for the API base URL use
http://host.docker.internal:11434/v1. Leave the API key field blank, there is nothing to authenticate against your own machine. When it asks to auto detect, say yes, and it lists whatever models Ollama has already pulled.
Pick a display name, choose a messaging platform if you want one wired in, and let the wizard finish. When it exits cleanly, the .hermes folder fills in with the rest of Hermes's working files, you do not create any of this by hand.
Bringing the container up
docker compose pull
docker compose up -dThe first command grabs the latest nousresearch/hermes-agent image, the second starts the container in the background. docker compose logs -f tails the logs if you want to watch it come up; it will say the dashboard is ready on port 9119.
Logging in and creating your first profile
Visit http://localhost:9119 and sign in with the username and password you set directly in compose.yaml. You land on a dashboard with a single default profile. Create a new one: give it a name, leave clone config as none, write a short description, and pick one of the model options you registered during setup.
Every profile has a soul.md, the personality and instruction file for that agent. The repo ships one for each of the three agents below, paste the matching one in and save. This one file is most of what separates one agent's behaviour from another's, everything else is shared infrastructure.
Connecting Hermes Desktop
The desktop app defaults to its own bundled gateway, you want it pointed at the one running in your container instead. Go to settings, then gateways, and under remote gateway enter http://localhost:9119 exactly. Signing in opens a browser window, log in with the same credentials again, then back in the desktop app save and reconnect. Once it settles, the version panel shows two entries, a client version for the desktop app and a backend version for the agent running inside Docker, confirmation that the two are actually talking to each other.
Three agents, three jobs
The repo's three soul.md files are not loose descriptions, they are strict instruction sets, and the difference between a good agent and a flaky one is almost entirely in how tightly that file is written.
Eevie, a filing clerk, not an assistant. Eevie's only job is sorting unsorted lines from a "brain dump" note into the right vault note, and her soul.md reads more like a small state machine than a personality: read the inbox note exactly once, build a fixed list of every unchecked line, then work through that list in order, classify each one against a fixed taxonomy table, append it to the right note, tick it off, and log one line to a ledger file. She never re-reads the inbox mid-run and never rewrites a note she has already appended to, both are hard rules in the file, not suggestions:
## Hard Rules (never break)
1. Never call write_file on `braindump.md`. Only patch, one line at a time, `- [ ]` → `- [x]`.
2. Never delete a line from `braindump.md`. Checked lines stay forever.
3. Never overwrite a destination note. Append only. Create it if missing.Run her on a local model. The input is your own unsorted thoughts, and there is no reason to send that to a cloud provider. She will not always classify perfectly on a 9B model, check _Hermes/ledger.md afterward and fix anything she got wrong.
Pokedex, a topic-opportunity researcher, not a general search agent. Give her a tool or a topic and she is not just summarising it, her job per soul.md is deciding whether it is worth a video at all: she checks search demand, checks who else has already covered it and how recently, then returns a ranked list of specific angles that are undercovered relative to their demand, plus the ones she is deliberately passing on and why. A free-tier cloud model is a reasonable fit here, there is no personal data in a research request.
Coder Bro, terse on purpose. He writes and edits code on request and nothing else, his soul.md explicitly rules out unrequested refactors, unrequested explanation, and any project file created outside /opt/data/coder_bro_data/coding_space. If you already pay for a ChatGPT or Codex subscription, that can back this profile instead of a separate API bill:
npm install -g @openai/codex
hermes auth add openai-codexThe auth command gives you a link and a code, complete that flow in a browser signed into your subscription, and Coder Bro can use it from then on.
Stopping it, and whether it needs to run all the time
docker compose down # stop
docker compose up -d # start againBringing the container down does not touch your data, everything lives in the bind-mounted folders on your actual disk, not inside the container. Whether any of this needs to run continuously depends entirely on your use case. A cron job that fires once a day does not need an always-on machine, a use case that genuinely needs 24/7 availability is better served by a small VPS or a dedicated always-on machine than by leaving a laptop running.
Where to go from here
Everything above is one specific setup, not the only correct one. The full walkthrough, including the on-screen bind mount diagram and the live demo of all three agents, is in the video below. The companion repo has the exact files, clone it rather than retyping any of the blocks above by hand.