Spaces:
Paused
Paused
| # Standalone NVIDIA GPU Compose file for stack-management UIs (Portainer, | |
| # Coolify, Dockhand, etc.) that accept only a single Compose file and do not | |
| # reliably honor COMPOSE_FILE or multiple `-f` overlays. | |
| # | |
| # This is equivalent to: docker-compose.yml + docker/gpu.nvidia.yml. | |
| # The base docker-compose.yml plus the docker/gpu.nvidia.yml overlay remain | |
| # the source of truth — CLI users should keep using the COMPOSE_FILE overlay | |
| # workflow. Keep this file in sync with both when either changes. | |
| # | |
| # Requires the NVIDIA Container Toolkit on the host. See docker/gpu.nvidia.yml | |
| # for setup details. | |
| services: | |
| odysseus: | |
| build: . | |
| ports: | |
| - "${APP_BIND:-127.0.0.1}:${APP_PORT:-7000}:7000" | |
| volumes: | |
| - ./data:/app/data:z | |
| - ./logs:/app/logs:z | |
| # Cookbook remote-server SSH identity. Odysseus can generate a key here; | |
| # add the shown public key to each remote server's authorized_keys. | |
| - ./data/ssh:/app/.ssh:z | |
| # Cookbook local model cache. Inside Docker, "Local" means the Odysseus | |
| # container, so persist its HuggingFace cache under ./data/huggingface. | |
| - ./data/huggingface:/app/.cache/huggingface:z | |
| # Cookbook-installed Python CLIs/packages (vLLM, llama-cpp-python, etc.) | |
| # land under /app/.local for the odysseus user. Persist them so a | |
| # container recreate does not silently remove installed serve engines. | |
| - ./data/local:/app/.local:z | |
| extra_hosts: | |
| # Lets the container reach local services on the Docker host, including | |
| # Ollama at http://host.docker.internal:11434. | |
| - "host.docker.internal:host-gateway" | |
| environment: | |
| - LLM_HOST=${LLM_HOST:-localhost} | |
| - LLM_HOSTS=${LLM_HOSTS:-} | |
| - OPENAI_API_KEY=${OPENAI_API_KEY:-} | |
| - OLLAMA_BASE_URL=${OLLAMA_BASE_URL:-} | |
| - RESEARCH_LLM_ENDPOINT=${RESEARCH_LLM_ENDPOINT:-} | |
| - HF_TOKEN=${HF_TOKEN:-} | |
| - HUGGING_FACE_HUB_TOKEN=${HUGGING_FACE_HUB_TOKEN:-} | |
| - SEARXNG_INSTANCE=http://searxng:8080 | |
| - CHROMADB_HOST=chromadb | |
| - CHROMADB_PORT=8000 | |
| - DATABASE_URL=${DATABASE_URL:-sqlite:///./data/app.db} | |
| - AUTH_ENABLED=${AUTH_ENABLED:-true} | |
| - LOCALHOST_BYPASS=${LOCALHOST_BYPASS:-false} | |
| - ODYSSEUS_ADMIN_USER=${ODYSSEUS_ADMIN_USER:-admin} | |
| - ODYSSEUS_ADMIN_PASSWORD=${ODYSSEUS_ADMIN_PASSWORD:-} | |
| - ALLOWED_ORIGINS=${ALLOWED_ORIGINS:-http://localhost,http://127.0.0.1} | |
| - SECURE_COOKIES=${SECURE_COOKIES:-false} | |
| - EMBEDDING_URL=${EMBEDDING_URL:-} | |
| - EMBEDDING_MODEL=${EMBEDDING_MODEL:-} | |
| - EMBEDDING_API_KEY=${EMBEDDING_API_KEY:-} | |
| - FASTEMBED_MODEL=${FASTEMBED_MODEL:-sentence-transformers/all-MiniLM-L6-v2} | |
| - FASTEMBED_CACHE_PATH=${FASTEMBED_CACHE_PATH:-} | |
| - CLEANUP_INTERVAL_HOURS=${CLEANUP_INTERVAL_HOURS:-24} | |
| - ODYSSEUS_INPROCESS_POLLERS=${ODYSSEUS_INPROCESS_POLLERS:-1} | |
| - ODYSSEUS_INPROCESS_TASKS=${ODYSSEUS_INPROCESS_TASKS:-1} | |
| - ODYSSEUS_SCRIPT_HOST=${ODYSSEUS_SCRIPT_HOST:-localhost} | |
| - ODYSSEUS_CHAT_UPLOAD_MAX_BYTES=${ODYSSEUS_CHAT_UPLOAD_MAX_BYTES:-10485760} | |
| - DATA_BRAVE_API_KEY=${DATA_BRAVE_API_KEY:-} | |
| - GOOGLE_API_KEY=${GOOGLE_API_KEY:-} | |
| - GOOGLE_PSE_CX=${GOOGLE_PSE_CX:-} | |
| - TAVILY_API_KEY=${TAVILY_API_KEY:-} | |
| - SERPER_API_KEY=${SERPER_API_KEY:-} | |
| # PUID / PGID — the user/group the container drops to before | |
| # running uvicorn (entrypoint also chowns /app/data + /app/logs | |
| # to match, so bind-mounted files stay editable from the host). | |
| # 1000 is the default first user on most Linux installs. If your | |
| # host user has a different id, override here or via .env, e.g.: | |
| # PUID=1001 | |
| # PGID=1001 | |
| # Find yours with: id -u / id -g | |
| - PUID=${PUID:-1000} | |
| - PGID=${PGID:-1000} | |
| # NVIDIA overlay (from docker/gpu.nvidia.yml). | |
| - NVIDIA_VISIBLE_DEVICES=all | |
| - NVIDIA_DRIVER_CAPABILITIES=compute,utility | |
| depends_on: | |
| searxng: | |
| condition: service_healthy | |
| chromadb: | |
| condition: service_started | |
| restart: unless-stopped | |
| # NVIDIA overlay (from docker/gpu.nvidia.yml). | |
| deploy: | |
| resources: | |
| reservations: | |
| devices: | |
| - driver: nvidia | |
| count: all | |
| capabilities: [gpu] | |
| chromadb: | |
| image: docker.io/chromadb/chroma:latest | |
| ports: | |
| - "${CHROMADB_BIND:-127.0.0.1}:8100:8000" | |
| volumes: | |
| - chromadb-data:/chroma/chroma | |
| environment: | |
| - ANONYMIZED_TELEMETRY=FALSE | |
| restart: unless-stopped | |
| searxng: | |
| # Pinned, not :latest — odysseus waits on searxng's healthcheck | |
| # (depends_on: condition: service_healthy), so a broken upstream `latest` | |
| # tag blocks the whole app from starting. 2026.6.2 crashes on boot with | |
| # `KeyError: 'default_doi_resolver'`, failing the healthcheck (issue #1414). | |
| # Bump this deliberately after verifying a newer tag boots clean. | |
| image: docker.io/searxng/searxng:2026.5.31-7159b8aed | |
| entrypoint: | |
| - /bin/sh | |
| - -c | |
| - | | |
| set -eu | |
| if [ ! -s /etc/searxng/settings.yml ] || grep -q 'odysseus-local-searxng-json-2026-05-30\|__SEARXNG_SECRET__' /etc/searxng/settings.yml; then | |
| secret="$${SEARXNG_SECRET:-}" | |
| if [ -z "$$secret" ]; then | |
| secret="$$(python -c 'import secrets; print(secrets.token_urlsafe(48))')" | |
| fi | |
| sed "s|__SEARXNG_SECRET__|$$secret|g" /tmp/searxng-settings.yml.template > /etc/searxng/settings.yml | |
| fi | |
| exec /usr/local/searxng/entrypoint.sh | |
| ports: | |
| - "127.0.0.1:8080:8080" | |
| volumes: | |
| - searxng-data:/etc/searxng | |
| - ./config/searxng/settings.yml:/tmp/searxng-settings.yml.template:ro,z | |
| environment: | |
| - SEARXNG_BASE_URL=http://localhost:8080/ | |
| - SEARXNG_SECRET=${SEARXNG_SECRET:-} | |
| # The official searxng image runs as the non-root `searxng` user, but its | |
| # entrypoint still needs to chown /etc/searxng on first boot, drop privs via | |
| # su-exec, and (with our wrapper above) write settings.yml into the named | |
| # volume. Without these capabilities the wrapper aborts at the redirection | |
| # with EACCES and the container fails its healthcheck with permission | |
| # errors during setup. Mirrors the cap set recommended by the upstream | |
| # searxng-docker compose file. See issue #721. | |
| cap_drop: | |
| - ALL | |
| cap_add: | |
| - CHOWN | |
| - SETGID | |
| - SETUID | |
| - DAC_OVERRIDE | |
| healthcheck: | |
| test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:8080/', timeout=5).read(1)\""] | |
| interval: 5s | |
| timeout: 6s | |
| retries: 20 | |
| start_period: 10s | |
| restart: unless-stopped | |
| ntfy: | |
| image: docker.io/binwiederhier/ntfy | |
| command: serve | |
| ports: | |
| - "${NTFY_BIND:-127.0.0.1}:8091:80" | |
| volumes: | |
| - ntfy-cache:/var/cache/ntfy | |
| environment: | |
| - NTFY_BASE_URL=${NTFY_BASE_URL:-http://localhost:8091} | |
| restart: unless-stopped | |
| volumes: | |
| searxng-data: | |
| chromadb-data: | |
| ntfy-cache: | |