Sign inSign up

emmaylots/hermes-agent-webui

By emmaylots

•Updated 20 days ago

Private, self-hosted web UI for Hermes Agent (unofficial). Single-user AI agent, local with Ollama.

Image
Machine learning & AI
0

716

emmaylots/hermes-agent-webui repository overview

⁠Hermes Agent WebUI: your private, self-hosted AI agent

A private web UI for Hermes Agent⁠ that runs entirely on your own machine. Chat with an agent that can use tools, work with your files and remember you, powered by local models through Ollama. There's no cloud account, no telemetry, and nothing leaves your network unless you choose to.

Unofficial community project, not affiliated with or endorsed by Nous Research. Hermes Agent is Nous Research's open-source agent; this image is a web front end for it.

Built for one person: a personal agent you run at home or on your own server, installable as an app on desktop and mobile.

⁠Features

  • Chat: streaming replies, Markdown and code blocks, a per-conversation model picker, and a live view of the agent's tool activity.
  • Scheduled tasks: have Hermes do things later or on a schedule (once, every few hours, daily, weekdays, weekly, monthly or a cron expression), with a list and a month/week calendar. Each run is saved as a chat you can read and continue.
  • Projects: group chats into folders (e.g. Farming), with optional per-project instructions.
  • Files: a shared /workspace that you and the agent both use. Upload, preview (text, Markdown, images, PDF, audio/video), rename, delete, and attach files to messages.
  • Skills and tools:
    • browse Hermes's skills, and create or edit your own
    • type / in the message box to pick a skill
    • switch tool groups (web, browser, terminal, memory…) on or off
  • Models:
    • set the default model
    • keep background tasks on your local model
    • add provider API keys if you ever want a cloud model
  • Your agent, your persona: edit Hermes's SOUL.md, and share chosen profile details with its memory.
  • Account security: password sign-in, two-factor authentication (authenticator app, QR code), one-time recovery codes, and a sign-in attempt limit.
  • Inbox: Hermes can send you messages, such as finished reports, reminders and alerts, with the files it saved in the workspace attached. Install the included skill from the Inbox page, then ask "message me when…" or "send me the report". Reply opens a chat with the message and its files.
  • Notifications: optional browser notifications when a reply or a scheduled task finishes while the app is in the background (needs HTTPS; previews are off by default). The tab title always shows what finished while you were away.
  • Installable app (PWA): light and dark themes, responsive down to phones.

⁠Privacy by design

  • Local models by default. The sample stack runs Ollama next to Hermes. Background jobs (titles, compression, web extraction) are set to your local model, so they never fall back to cloud providers.
  • Private by default. The UI is published on 127.0.0.1 unless you choose otherwise, and Hermes and Ollama aren't published at all. For access from your other devices, the recommended setup is HTTPS over your own Tailscale network, with Caddy and a real certificate, and nothing exposed to the internet.
  • No trackers, no CDNs. All assets are bundled, and nothing is loaded from third-party servers.
  • Secrets stay put. The Hermes API key comes from the environment. Two-factor secrets are encrypted at rest (AES-256-GCM). Hermes's private data (config, memories, credentials) is never exposed through the web UI.
  • Hardened container. Read-only filesystem, all Linux capabilities dropped, no-new-privileges, non-root user.

⁠Quick start (Docker Compose, with Ollama)

  1. Save the two files below into an empty folder.
  2. Fill in the two secrets in .env.
  3. Start the stack:
docker compose up -d
docker compose exec ollama ollama pull qwen3.5:4b     # the model named in OLLAMA_MODEL
  1. Open http://localhost:8080⁠ and create your account (or your https://….ts.net address; see Remote access).
  2. Optional: turn on two-factor authentication under Account.

docker-compose.yml

# Hermes Agent WebUI: private, single-user personal agent stack
#
#   Ollama (local models) → Hermes Agent (agent, tools, memory) → Hermes Agent WebUI (browser UI)
#
# Everything runs on your machine. Hermes and Ollama are never published; the web UI is published on
# 127.0.0.1 by default, and the optional Caddy service serves it over HTTPS on your Tailscale tailnet.
# Quick start:
#   cp .env.example .env        # then fill in the two secrets (see comments in the file)
#   docker compose up -d
#   docker compose exec ollama ollama pull qwen3.5:4b   # or the model you set as OLLAMA_MODEL
#   open http://localhost:8080 and create your account
# Remote access (recommended): set COMPOSE_PROFILES=caddy, TS_DOMAIN and TAILSCALE_IP in .env, see below.
name: hermes

services:
  # ── Local model server ────────────────────────────────────────────────────────
  ollama:
    image: ollama/ollama:latest
    restart: unless-stopped
    volumes:
      - ollama:/root/.ollama
    networks: [hermes]
    # NVIDIA GPU (needs the NVIDIA Container Toolkit): uncomment
    # deploy:
    #   resources:
    #     reservations:
    #       devices:
    #         - driver: nvidia
    #           count: all
    #           capabilities: [gpu]

  # ── One-time setup: shared workspace permissions and first-run Hermes defaults ─
  # The defaults are applied once (marker file in Hermes's data volume), so changes you make
  # later, e.g. on the web UI's Models page, are never overwritten.
  setup:
    image: nousresearch/hermes-agent:v2026.8.16
    entrypoint: ["/bin/sh", "-c"]
    command:
      - |
        set -e
        chmod 0777 /workspace && mkdir -p /workspace/uploads /workspace/outputs && chmod 0777 /workspace/uploads /workspace/outputs
        h=/opt/hermes/bin/hermes
        # Browsing: with browser.backend unset, Hermes picks the Browser Use CLI, which needs an
        # already-running Chrome or a CDP URL and fails in a headless container. Use Hermes's built-in
        # browser tools (bundled headless Chromium) unless you chose a backend yourself. Runs on every
        # start, so existing deployments pick it up too.
        backend=$$($$h config get browser.backend 2>/dev/null || true)
        if [ -z "$$backend" ]; then $$h config set browser.backend off >/dev/null && echo "Browser: built-in headless Chromium (browser.backend=off)"; fi
        marker=/opt/data/.hermes-webui-defaults
        if [ -f "$$marker" ]; then echo "Hermes defaults already applied; nothing to do."; exit 0; fi
        unset_or() { $$h config get "$$1" >/dev/null 2>&1 || $$h config set "$$1" "$$2" >/dev/null; }
        # Model: the bundled (or your own) Ollama, unless a provider is already configured.
        unset_or model.provider custom
        unset_or model.base_url "$${OLLAMA_BASE_URL}"
        unset_or model.default "$${OLLAMA_MODEL}"
        unset_or model.api_mode chat_completions
        # Hermes works in the shared workspace, so its files appear in the web UI's file manager.
        $$h config set terminal.cwd /workspace >/dev/null
        # Privacy: background jobs (titles, compression, web extraction…) use your local model
        # and never fall back to cloud providers.
        for task in vision web_extract compression skills_hub approval mcp title_generation triage_specifier kanban_decomposer profile_describer curator; do
          $$h config set "auxiliary.$$task.provider" main >/dev/null
        done
        touch "$$marker"
        echo "Hermes defaults applied: model=$$($$h config get model.default) via $$($$h config get model.base_url), cwd=/workspace, background tasks=main"
    environment:
      OLLAMA_BASE_URL: ${OLLAMA_BASE_URL:-http://ollama:11434/v1}
      OLLAMA_MODEL: ${OLLAMA_MODEL:-qwen3.5:4b}
    volumes:
      - hermes-data:/opt/data
      - workspace:/workspace
    restart: "no"
    networks: [hermes]

  # ── The agent ─────────────────────────────────────────────────────────────────
  hermes:
    image: nousresearch/hermes-agent:v2026.8.16
    command: gateway run
    restart: unless-stopped
    shm_size: 1gb   # headless Chromium for the browser tools; Docker's 64 MB default can crash on heavy pages
    environment:
      API_SERVER_ENABLED: "true"
      API_SERVER_HOST: 0.0.0.0
      API_SERVER_PORT: "8642"
      API_SERVER_KEY: ${HERMES_API_KEY:?Set HERMES_API_KEY in .env}
      API_SERVER_MODEL_NAME: ${OLLAMA_MODEL:-qwen3.5:4b}
      HERMES_TIMEZONE: ${HERMES_TIMEZONE:-}   # clock and scheduled tasks; UTC when empty
      # Hermes's file tools may write in its data dir and the shared workspace (reports, outputs).
      # The image default (/opt/data only) makes it fall back to the terminal for workspace files.
      HERMES_WRITE_SAFE_ROOT: /opt/data:/workspace
    volumes:
      - hermes-data:/opt/data
      - workspace:/workspace
    depends_on:
      setup:
        condition: service_completed_successfully
      ollama:
        condition: service_started
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8642/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s
    networks: [hermes]

  # ── The web UI ────────────────────────────────────────────────────────────────
  hermes-webui:
    image: ${HERMES_WEBUI_IMAGE:-emmaylots/hermes-agent-webui:latest}
    restart: unless-stopped
    ports:
      - "${WEB_BIND:-127.0.0.1}:${WEB_PORT:-8080}:8080"   # loopback by default; use the caddy profile for remote HTTPS
    environment:
      APP_NAME: ${APP_NAME:-Hermes}
      HERMES_GATEWAY_URL: http://hermes:8642
      HERMES_API_KEY: ${HERMES_API_KEY:?Set HERMES_API_KEY in .env}
      HERMES_WEB_SECRET: ${HERMES_WEB_SECRET:?Set HERMES_WEB_SECRET in .env}
      HERMES_COOKIE_SECURE: ${HERMES_COOKIE_SECURE:-false}   # true when served over HTTPS
      HERMES_TIMEZONE: ${HERMES_TIMEZONE:-}   # must match Hermes's, so scheduled times line up
    volumes:
      - webui-data:/data
      - workspace:/workspace
    read_only: true
    tmpfs: ["/tmp"]
    cap_drop: [ALL]
    security_opt: [no-new-privileges:true]
    depends_on:
      hermes:
        condition: service_healthy
    networks: [hermes]

  # ── Optional: HTTPS on your tailnet with Caddy + Tailscale certificates ──────────
  # Enable with COMPOSE_PROFILES=caddy. Needs Tailscale on the host with MagicDNS and
  # "HTTPS Certificates" turned on in the Tailscale admin console (DNS page). Caddy obtains the
  # *.ts.net certificate from the host's tailscaled through its socket, so no port 80 or public
  # DNS is needed, and binding to the Tailscale IP keeps the UI reachable only from your tailnet.
  caddy:
    image: caddy:2
    profiles: [caddy]
    restart: unless-stopped
    ports:
      - "${TAILSCALE_IP:-127.0.0.1}:443:443"
    volumes:
      - /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock
      - caddy-data:/data
    configs:
      - source: caddyfile
        target: /etc/caddy/Caddyfile
    depends_on: [hermes-webui]
    networks: [hermes]

configs:
  caddyfile:
    content: |
      ${TS_DOMAIN:-hermes.example.ts.net} {
        encode zstd gzip
        header {
          Strict-Transport-Security "max-age=31536000"
          X-Content-Type-Options nosniff
          Referrer-Policy no-referrer
          -Server
        }
        reverse_proxy hermes-webui:8080
      }

networks:
  hermes:

volumes:
  caddy-data:
  ollama:
  hermes-data:
  webui-data:
  workspace:

.env

# Copy to .env and fill in the two secrets. Never commit .env.

# Key the web UI uses to talk to Hermes (any long random string). Generate one with:
#   openssl rand -hex 32
HERMES_API_KEY=

# Encrypts stored secrets (two-factor keys). Generate with `openssl rand -hex 32` and keep it stable.
HERMES_WEB_SECRET=

# Local model served by Ollama (pull it once: docker compose exec ollama ollama pull <model>).
# Any tool-calling model works; larger models give better agent results.
OLLAMA_MODEL=qwen3.5:4b

# Use an Ollama that is already running elsewhere instead of the bundled one:
# OLLAMA_BASE_URL=http://192.168.1.10:11434/v1

# Your time zone (IANA name). Hermes uses it for its clock and for scheduled tasks; without it,
# Hermes runs on UTC and the web UI converts the times you pick.
# HERMES_TIMEZONE=Africa/Johannesburg

# Web UI address and port on this machine (127.0.0.1 = this machine only) and display name.
# WEB_BIND=127.0.0.1
# WEB_PORT=8080
# APP_NAME=Hermes

# ── Recommended remote access: HTTPS on your Tailscale tailnet via Caddy ──────────
# Needs Tailscale on the host with MagicDNS and HTTPS Certificates enabled (admin console → DNS).
# COMPOSE_PROFILES=caddy
# TS_DOMAIN=myserver.tail1234.ts.net      # this machine's name: tailscale status --self
# TAILSCALE_IP=100.x.y.z                  # this machine's Tailscale IP: tailscale ip -4
# HERMES_COOKIE_SECURE=true               # required when served over HTTPS

Generate the secrets with openssl rand -hex 32.

⁠Already running Ollama?

Set OLLAMA_BASE_URL=http://<host>:11434/v1 in .env. You can then remove the ollama service and its depends_on entry.

⁠Choosing a model

Hermes is an agent: it calls tools, so pick a tool-calling model. Small models (3–9B) work for everyday tasks, and larger ones are noticeably better at multi-step work. You can switch later on the Models page, or per conversation from the model picker.

⁠Configuration (hermes-webui service)

VariableRequiredDescription
HERMES_API_KEYyesKey for the Hermes API server; must equal Hermes's API_SERVER_KEY (the sample wires both from .env)
HERMES_WEB_SECRETyesEncrypts stored secrets; keep it stable
HERMES_GATEWAY_URLnoHermes API server URL (default http://hermes:8642)
HERMES_COOKIE_SECUREnotrue when served over HTTPS
APP_NAMEnoName shown in the UI (default Hermes)
HERMES_UPLOAD_MAX_BYTESnoUpload limit (default 50 MB)
HERMES_TIMEZONEnoYour IANA time zone (e.g. Africa/Johannesburg). Give Hermes the same value (the sample wires both from .env). Without it, Hermes runs on UTC and the web UI converts the times you pick for scheduled tasks

Volumes: /data holds the web UI's accounts, projects and settings; /workspace is the folder shared with Hermes.

Reach your agent from your laptop and phone over your own Tailscale network, with a real HTTPS certificate and without opening anything to the internet. The sample Compose file includes an optional caddy service. Caddy gets a trusted *.ts.net certificate from the Tailscale daemon on the host, so no port 80, public DNS or port forwarding is needed.

  1. Install Tailscale on the Docker host and sign in.
  2. In the Tailscale admin console, open DNS and turn on MagicDNS and HTTPS Certificates.
  3. Add to .env:
    COMPOSE_PROFILES=caddy
    TS_DOMAIN=myserver.tail1234.ts.net   # from: tailscale status --self
    TAILSCALE_IP=100.x.y.z               # from: tailscale ip -4
    HERMES_COOKIE_SECURE=true
    
  4. docker compose up -d, then open https://myserver.tail1234.ts.net⁠ from any device on your tailnet.

Caddy listens only on the Tailscale IP, so the UI isn't reachable from your LAN or the internet. It also adds HSTS, nosniff and no-referrer headers. The web UI's own port stays on 127.0.0.1 for local use; you can change it with WEB_BIND.

If you prefer another reverse proxy (Traefik, nginx, Cloudflare Tunnel), point it at hermes-webui:8080 and set HERMES_COOKIE_SECURE=true when serving over HTTPS.

⁠Updating and backups

docker compose pull && docker compose up -d

Back up the hermes-data, webui-data and workspace volumes. Hermes keeps a backup of anything the web UI changes: SOUL.md, memory and skills in /workspace/backups, and config inside Hermes's data volume.

⁠Good to know

  • Single user by design. Everyone who can sign in shares the same Hermes agent, workspace and conversations.
  • Web browsing uses Hermes's built-in browser tools with the bundled headless Chromium. The setup step sets browser.backend: off when it's unset, because Hermes otherwise defaults to its Browser Use CLI mode, which needs an already-running Chrome or browser.cdp_url and fails in a plain container. If you use Browser Use (a cloud browser or your own Chrome via CDP), set browser.backend yourself and the setup step leaves it alone.
  • Hermes can act. With the terminal and file tools on, it can run commands and edit files inside its container and the shared workspace. Only give access to people you trust.
  • Platforms: linux/amd64 and linux/arm64 (Intel/AMD PCs and servers, Raspberry Pi 4/5, Apple Silicon, ARM cloud servers). Docker picks the right one automatically, and the whole sample stack (Ollama, Hermes, Caddy) supports both.
  • The default model can be switched between models on the same endpoint from the UI. Changing to a different provider is done with hermes model in the Hermes container, followed by a restart.

⁠Changelog

⁠0.4.0
  • Inbox: Hermes can send you messages (reports, reminders, alerts) with the files it saved in the workspace attached. Install the included skill from the Inbox page, then restart Hermes once. Reply opens a chat with the message and its files.
  • Scheduled tasks: new "Send the result to my Inbox" option.
  • Notifications: optional browser notifications when a reply, a scheduled run or an inbox message arrives while the app is in the background (HTTPS only; reply previews off by default). The tab title shows an unread count on any connection.
  • Fixed: starting a chat whose opening words matched an existing chat's title failed with "Title already in use".
  • Fixed: "…" menus in a long sidebar could close before you clicked an item.
  • Sample compose: HERMES_WRITE_SAFE_ROOT: /opt/data:/workspace, so Hermes's file tools can save reports in the workspace. Add it to existing deployments.
  • After creating a skill, the Skills page now says Hermes needs a restart to list it.
⁠0.3.0
  • Scheduled tasks: create, edit, pause, run now and delete Hermes cron jobs (once, every N minutes/hours/days, daily, weekdays, weekly, monthly or a cron expression), with a list and a month/week calendar.
  • Each run is saved as a chat under Scheduled runs in the sidebar, without Hermes's internal cron instructions.
  • Time zones: times are picked in your time zone and converted for Hermes. New optional HERMES_TIMEZONE setting for Hermes and the web UI.
⁠0.2.2
  • Fixed: leaving a chat while Hermes was working and coming back showed blank "•••" replies or lost the live reply.
  • Saved agent turns show as a single reply with a "Used: terminal ×2" line instead of several empty bubbles.
⁠0.2.1
  • Fixed: the chat failed on plain-HTTP LAN addresses (browsers only offer some APIs over HTTPS); copy buttons now work there too.
  • Sample compose: Hermes uses its built-in headless browser (browser.backend off) and gets 1 GB of shared memory, so web browsing works in Docker.
⁠0.2.0
  • First public release: chat with streaming and tool activity, projects, workspace file manager, skills, tools and model management, account security with two-factor authentication, installable app, and a private-by-default sample stack with Ollama and optional Caddy + Tailscale HTTPS.

⁠License

MIT. The license text ships in the image at /app/LICENSE. Hermes Agent, Ollama and Caddy are separate projects under their own licenses.

Tag summary

Content type

Image

Digest

sha256:a733991ce…

Size

76.3 MB

Last updated

20 days ago

docker pull emmaylots/hermes-agent-webui