diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 00000000000..a0e9ae59ce1 --- /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: docker/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 00000000000..949dc45eaff --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM debian + +RUN apt update +RUN apt 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 pip install -e "./mini-swe-agent" --break-system-packages +# RUN pip install -e "./tinker-atropos" --break-system-packages +RUN npm install +RUN npx playwright install --with-deps chromium + +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 00000000000..9103a6122ee --- /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 00000000000..554d53188cb --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,31 @@ +#!/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 directory structure +mkdir -p "$HERMES_HOME"/{cron,sessions,logs,pairing,hooks,image_cache,audio_cache,memories,skills,whatsapp/session} + +# .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/docs/docker.md b/docs/docker.md new file mode 100644 index 00000000000..22991977424 --- /dev/null +++ b/docs/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 +```