Files
2026-09-06 13:51:06 +00:00

3.8 KiB

Memory OS Deployment — Session Details

Environment

  • Host: Linux, no Docker Desktop
  • Ollama: Docker container on ollama_default network, port 11434
  • Worker: Docker Compose stack with Qdrant + Redis + ARQ worker
  • Obsidian vault: /opt/hermes/obsidian-vault/
  • Wiki path: /opt/hermes/vault/wiki/raw/obsidian/
  • State file: ~/.hermes/wiki_ingest_state.json (tracks ingested files by hash)
  • Sync state: ~/.hermes/obsidian_sync_state.json

Docker Compose Path

/opt/hermes/memory-os/docker/docker-compose.yml

.env File

/opt/hermes/memory-os/docker/.env — contains:

  • OLLAMA_BASE_URL=http://ollama:11434
  • OLLAMA_MODEL=qwen3-8b-64k
  • OLLAMA_EMBEDDING_MODEL=nomic-embed-text:latest
  • EMBEDDING_API_BASE=http://ollama:11434/v1
  • EMBEDDING_DIMS=768
  • REDIS_PASSWORD, QDRANT_API_KEY, OPENROUTER_API_KEY

Error: Reflection Failing

Symptom: j_failed=90 on worker, all from cron:process_reflection

Error:

httpx.ConnectError: [Errno -2] Name or service not known

The worker was trying http://host.docker.internal:11434/api/generate

Root cause: OLLAMA_BASE_URL defaulted to http://host.docker.internal:11434 in services/llm.py:

OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "http://host.docker.internal:11434")

This variable was not listed in docker-compose.yml under worker.environment, so the container fell back to the hardcoded default.

Fix:

  1. Added OLLAMA_BASE_URL: ${OLLAMA_BASE_URL:-http://ollama:11434} and OLLAMA_MODEL: ${OLLAMA_MODEL:-qwen3-8b-64k} to docker-compose.yml
  2. Added OLLAMA_MODEL=qwen3-8b-64k to .env
  3. Connected worker to ollama_default external network in compose
  4. docker compose up -d --force-recreate worker to rebuild

Network Layout

worker (memory-os_default: 172.24.0.x, ollama_default: 172.18.0.3)
  → ollama (ollama_default: 172.18.0.5) via DNS
  → qdrant (memory-os_default) via DNS
  → redis (memory-os_default) via DNS
search-api (memory-os_default: 172.24.0.x, ollama_default: 172.18.0.x)
  → ollama (ollama_default) via DNS
  → qdrant (memory-os_default) via DNS

Qdrant Collection

  • Name: knowledge_base
  • Points: 683
  • Dense vector: 768 dims, Cosine distance
  • Sparse vector: BM25 (on_disk=True)
  • Embedding model: nomic-embed-text:latest

Search API

Path: /opt/hermes/memory-os/search_api/main.py Port: localhost:8000 Docker image: docker-search-api (built from search_api/Dockerfile)

Files:

search_api/
├── Dockerfile
├── requirements.txt   # fastapi, uvicorn, httpx, pydantic
└── main.py            # FastAPI app with POST /search and GET /health

Endpoints:

  • GET /health → {"status": "ok", "qdrant": true, "ollama": true}
  • POST /search → body: {"query": "search text", "top_k": 3} → {"query": "...", "results": [...], "total": N}

Cron Jobs

sync+ingest (LLM-driven, every 10m)

  • Name: memory-os obsidian sync + ingest
  • Schedule: every 10 minutes
  • Prompt: Runs sync script + ingest pipeline
  • Tools: terminal only

micro-reflection trigger (no_agent, every 5m, silent)

  • Name: memory-os micro-reflection trigger
  • Schedule: */5 * * * *
  • Script: docker exec docker-worker-1 python3 /app/scripts/reflection_trigger.py
  • no_agent: true — no LLM tokens, just runs the script
  • deliver: local — silent, no Telegram notifications
  • Script path must be mounted into container: - ../scripts:/app/scripts:ro in docker-compose.yml

Scripts

  • scripts/test_qdrant_search.py — standalone test: takes --query, gets embedding, searches Qdrant, prints top-5
  • scripts/sync_obsidian_to_wiki.py — copies .md from Obsidian vault to wiki path, tracks state
  • scripts/wiki_continuous_ingest.py — detects new/changed files, enqueues to ARQ worker
  • scripts/reflection_trigger.py — checks idle, respects budget, enqueues micro-reflection