146 lines
5.8 KiB
YAML
146 lines
5.8 KiB
YAML
services:
|
|
odysseus:
|
|
image: ${ODYSSEUS_IMAGE:-git.wilkensxl.de/mrsphay/odysseus:latest}
|
|
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:-}
|
|
- 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}
|
|
- 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}
|
|
depends_on:
|
|
searxng:
|
|
condition: service_healthy
|
|
chromadb:
|
|
condition: service_started
|
|
restart: unless-stopped
|
|
|
|
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:
|