From dcbdfdbb2b02f708d235fccce94c95b85014fc68 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Sat, 28 Mar 2026 22:21:48 -0700 Subject: [PATCH] feat(docker): add Docker container for the agent (salvage #1841) (#3668) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a complete Docker packaging for Hermes Agent: - Dockerfile based on debian:13.4 with all deps - Entrypoint that bootstraps .env, config.yaml, SOUL.md on first run - CI workflow to build, test, and push to DockerHub - Documentation for interactive, gateway, and upgrade workflows Closes #850, #913. Changes vs original PR: - Removed pre-created legacy cache/platform dirs from entrypoint (image_cache, audio_cache, pairing, whatsapp/session) — these are now created on demand by the application using the consolidated layout from get_hermes_dir() - Moved docs from docs/docker.md to website/docs/user-guide/docker.md and added to Docusaurus sidebar Co-authored-by: benbarclay --- .dockerignore | 13 ++++++ .github/workflows/docker-publish.yml | 61 ++++++++++++++++++++++++++++ Dockerfile | 20 +++++++++ docker/SOUL.md | 15 +++++++ docker/entrypoint.sh | 34 ++++++++++++++++ website/docs/user-guide/docker.md | 56 +++++++++++++++++++++++++ website/sidebars.ts | 1 + 7 files changed, 200 insertions(+) create mode 100644 .dockerignore create mode 100644 .github/workflows/docker-publish.yml create mode 100644 Dockerfile create mode 100644 docker/SOUL.md create mode 100644 docker/entrypoint.sh create mode 100644 website/docs/user-guide/docker.md diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..a690443f --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +# Git +.git +.gitignore +.gitmodules + +# Dependencies +node_modules + +# CI/CD +.github + +# Environment files +.env \ No newline at end of file diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 00000000..11b98c3a --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,61 @@ +name: Docker Build and Publish + +on: + push: + branches: [main] + pull_request: + branches: [main] + +concurrency: + group: docker-${{ github.ref }} + cancel-in-progress: true + +jobs: + build-and-push: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build image + uses: docker/build-push-action@v6 + with: + context: . + file: Dockerfile + load: true + tags: nousresearch/hermes-agent:test + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Test image starts + run: | + docker run --rm \ + -v /tmp/hermes-test:/opt/data \ + --entrypoint /opt/hermes/docker/entrypoint.sh \ + nousresearch/hermes-agent:test --help + + - name: Log in to Docker Hub + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Push image + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: docker/build-push-action@v6 + with: + context: . + file: Dockerfile + push: true + tags: | + nousresearch/hermes-agent:latest + nousresearch/hermes-agent:${{ github.sha }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..61b725d3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM debian:13.4 + +RUN apt-get update +RUN apt-get install -y nodejs npm python3 python3-pip ripgrep ffmpeg gcc python3-dev libffi-dev + +COPY . /opt/hermes +WORKDIR /opt/hermes + +RUN pip install -e ".[all]" --break-system-packages +RUN npm install +RUN npx playwright install --with-deps chromium +WORKDIR /opt/hermes/scripts/whatsapp-bridge +RUN npm install + +WORKDIR /opt/hermes +RUN chmod +x /opt/hermes/docker/entrypoint.sh + +ENV HERMES_HOME=/opt/data +VOLUME [ "/opt/data" ] +ENTRYPOINT [ "/opt/hermes/docker/entrypoint.sh" ] \ No newline at end of file diff --git a/docker/SOUL.md b/docker/SOUL.md new file mode 100644 index 00000000..9103a612 --- /dev/null +++ b/docker/SOUL.md @@ -0,0 +1,15 @@ +# Hermes Agent Persona + + \ No newline at end of file diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100644 index 00000000..4c6366cb --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Docker entrypoint: bootstrap config files into the mounted volume, then run hermes. +set -e + +HERMES_HOME="/opt/data" +INSTALL_DIR="/opt/hermes" + +# Create essential directory structure. Cache and platform directories +# (cache/images, cache/audio, platforms/whatsapp, etc.) are created on +# demand by the application — don't pre-create them here so new installs +# get the consolidated layout from get_hermes_dir(). +mkdir -p "$HERMES_HOME"/{cron,sessions,logs,hooks,memories,skills} + +# .env +if [ ! -f "$HERMES_HOME/.env" ]; then + cp "$INSTALL_DIR/.env.example" "$HERMES_HOME/.env" +fi + +# config.yaml +if [ ! -f "$HERMES_HOME/config.yaml" ]; then + cp "$INSTALL_DIR/cli-config.yaml.example" "$HERMES_HOME/config.yaml" +fi + +# SOUL.md +if [ ! -f "$HERMES_HOME/SOUL.md" ]; then + cp "$INSTALL_DIR/docker/SOUL.md" "$HERMES_HOME/SOUL.md" +fi + +# Sync bundled skills (manifest-based so user edits are preserved) +if [ -d "$INSTALL_DIR/skills" ]; then + python3 "$INSTALL_DIR/tools/skills_sync.py" +fi + +exec hermes "$@" diff --git a/website/docs/user-guide/docker.md b/website/docs/user-guide/docker.md new file mode 100644 index 00000000..22991977 --- /dev/null +++ b/website/docs/user-guide/docker.md @@ -0,0 +1,56 @@ +# Hermes Agent — Docker + +Want to run Hermes Agent, but without installing packages on your host? This'll sort you out. + +This will let you run the agent in a container, with the most relevant modes outlined below. + +The container stores all user data (config, API keys, sessions, skills, memories) in a single directory mounted from the host at `/opt/data`. The image itself is stateless and can be upgraded by pulling a new version without losing any configuration. + +## Quick start + +If this is your first time running Hermes Agent, create a data directory on the host and start the container interactively to run the setup wizard: + +```sh +mkdir -p ~/.hermes +docker run -it --rm \ + -v ~/.hermes:/opt/data \ + nousresearch/hermes-agent +``` + +This drops you into the setup wizard, which will prompt you for your API keys and write them to `~/.hermes/.env`. You only need to do this once. It is highly recommended to set up a chat system for the gateway to work with at this point. + +## Running in gateway mode + +Once configured, run the container in the background as a persistent gateway (Telegram, Discord, Slack, WhatsApp, etc.): + +```sh +docker run -d \ + --name hermes \ + --restart unless-stopped \ + -v ~/.hermes:/opt/data \ + nousresearch/hermes-agent gateway run +``` + +## Running interactively (CLI chat) + +To open an interactive chat session against a running data directory: + +```sh +docker run -it --rm \ + -v ~/.hermes:/opt/data \ + nousresearch/hermes-agent +``` + +## Upgrading + +Pull the latest image and recreate the container. Your data directory is untouched. + +```sh +docker pull nousresearch/hermes-agent:latest +docker rm -f hermes +docker run -d \ + --name hermes \ + --restart unless-stopped \ + -v ~/.hermes:/opt/data \ + nousresearch/hermes-agent +``` diff --git a/website/sidebars.ts b/website/sidebars.ts index c7fef2ae..10f34cd0 100644 --- a/website/sidebars.ts +++ b/website/sidebars.ts @@ -37,6 +37,7 @@ const sidebars: SidebarsConfig = { 'user-guide/configuration', 'user-guide/sessions', 'user-guide/security', + 'user-guide/docker', { type: 'category', label: 'Messaging Gateway',