How to Sandbox Hermes with Docker Desktop?
The familiar route — run Hermes inside a Docker container on macOS Sonoma, with one bind-mounted folder as the only link to the host.
Ingredients
- A Mac running macOS Sonoma (14)
- Docker Desktop for Mac — check the licence for your situation
- Hermes
- About 15 GB of free disk
- One empty folder for the shared bridge
This is the choice when your team already runs Docker Desktop, when your CI builds the same container image, or when you simply prefer a stack that everyone has heard of. The trade-offs against OrbStack are slower file sharing, more idle RAM, and a licence to mind at larger organisations.
1 Install Docker Desktop
brew install --cask docker
Launch Docker Desktop once so it can install its helper and accept the licence terms. Verify the engine is up:
docker version
docker run --rm hello-world
2 Cap the VM resources
Open Docker Desktop → Settings → Resources → Advanced and set:
- CPUs: 4
- Memory: 4 GB
- Swap: 1 GB
- Virtual disk limit: 20 GB
Click Apply & restart. Docker Desktop will recreate the virtual disk to the new size.
3 Lock down the file-sharing list
By default Docker Desktop allows containers to bind-mount almost
anywhere under /Users. We want exactly one folder.
mkdir -p ~/hermes-workspace
In Settings → Resources → File sharing,
remove the broad /Users entry and add only:
/Users/<you>/hermes-workspace
Apply and restart. From now on any container that tries to mount a path outside that folder fails at start.
4 Write the Dockerfile
Pin the Python, install fnm, install Node, install Hermes. Keep this
in ~/hermes-image/Dockerfile on the Mac:
FROM python:3.12-slim-bookworm
ENV DEBIAN_FRONTEND=noninteractive \
HERMES_WORKSPACE=/workspace \
PATH=/root/.local/share/fnm:/root/.local/bin:$PATH
RUN apt-get update && apt-get install -y --no-install-recommends \
curl ca-certificates git build-essential unzip && \
rm -rf /var/lib/apt/lists/*
# Node via fnm, isolated to this image
RUN curl -fsSL https://fnm.vercel.app/install | bash -s -- --skip-shell && \
/root/.local/share/fnm/fnm install 22 && \
/root/.local/share/fnm/fnm default 22
# Hermes itself
RUN git clone https://github.com/NousResearch/hermes-agent.git /opt/hermes && \
pip install --no-cache-dir -e /opt/hermes
WORKDIR /workspace
CMD ["hermes", "run", "--workspace", "/workspace"]
Build the image:
cd ~/hermes-image
docker build -t hermes-sandbox:latest .
5 Run the container with one mount
docker run -it --rm \
--name hermes \
--memory=4g --cpus=4 \
--read-only \
--tmpfs /tmp:rw,size=512m \
--tmpfs /root:rw,size=1g \
-v ~/hermes-workspace:/workspace \
hermes-sandbox:latest
What each flag does:
--read-only— the container’s root filesystem is immutable for the lifetime of the run; only the explicit tmpfs and the mount are writable.--tmpfs /tmp,--tmpfs /root— ephemeral scratch space that vanishes when the container exits.-v ~/hermes-workspace:/workspace— the single bridge to the host.--memory,--cpus— belt-and-braces on top of the VM-level limits.
6 Verify the seam
On the host:
echo "hello from the mac" > ~/hermes-workspace/in.txt
Inside the container (in the running Hermes prompt or a second
shell with docker exec -it hermes bash):
cat /workspace/in.txt
echo "hello from the sandbox" > /workspace/out.txt
Back on the host:
cat ~/hermes-workspace/out.txt
That is the entire surface area of the sandbox.
Making it stick across reboots
--rm throws the container away when it exits, which is
usually what you want for safety. If you need state to survive
restarts — for example Hermes’ own model cache —
add a named volume inside the container instead of mounting
another host folder:
docker volume create hermes-state
docker run -it --rm \
--name hermes \
-v hermes-state:/var/lib/hermes \
-v ~/hermes-workspace:/workspace \
hermes-sandbox:latest
Named volumes live inside the Docker VM, never on your Mac filesystem.
What you end up with
A reproducible Hermes image that any teammate can rebuild from the same Dockerfile, capped at 20 GB of virtual disk, with one bind-mount as its only path to your Mac. Throw away the container any time — the workspace folder survives, everything else dies.
Back to the main recipe
← How to Run Hermes in a macOS Sandbox?