mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-09-10 18:22:20 +02:00
The migration runs under `set -eu`, so any settings file it cannot parse or rewrite took the container down instead of merely going unmigrated. A symlinked /etc/searxng/settings.yml is enough: the migration refuses a non-regular file and searxng, which reads through the symlink perfectly well, never got to start. Guard the call with `|| true` in all three Compose variants. The failure still prints its reason on stderr, and searxng is left to report anything genuinely wrong with the file.
186 lines
8.2 KiB
YAML
186 lines
8.2 KiB
YAML
# 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:
|
|
- ${APP_DATA_DIR:-./data}:/app/data:z
|
|
- ${APP_LOGS_DIR:-./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.
|
|
- ${APP_DATA_DIR:-./data}/ssh:/app/.ssh:z
|
|
# Cookbook local model cache. Inside Docker, "Local" means the Odysseus
|
|
# container, so persist its HuggingFace cache under ./data/huggingface.
|
|
- ${APP_DATA_DIR:-./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.
|
|
- ${APP_DATA_DIR:-./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}
|
|
- ODYSSEUS_GALLERY_UPLOAD_MAX_BYTES=${ODYSSEUS_GALLERY_UPLOAD_MAX_BYTES:-104857600}
|
|
- ODYSSEUS_GALLERY_TRANSFORM_UPLOAD_MAX_BYTES=${ODYSSEUS_GALLERY_TRANSFORM_UPLOAD_MAX_BYTES:-26214400}
|
|
- ODYSSEUS_MEMORY_IMPORT_MAX_BYTES=${ODYSSEUS_MEMORY_IMPORT_MAX_BYTES:-10485760}
|
|
- ODYSSEUS_PERSONAL_UPLOAD_MAX_BYTES=${ODYSSEUS_PERSONAL_UPLOAD_MAX_BYTES:-26214400}
|
|
- ODYSSEUS_EMAIL_COMPOSE_UPLOAD_MAX_BYTES=${ODYSSEUS_EMAIL_COMPOSE_UPLOAD_MAX_BYTES:-26214400}
|
|
- ODYSSEUS_STT_MAX_AUDIO_BYTES=${ODYSSEUS_STT_MAX_AUDIO_BYTES:-26214400}
|
|
- ODYSSEUS_ICS_MAX_BYTES=${ODYSSEUS_ICS_MAX_BYTES:-10485760}
|
|
- ODYSSEUS_TTS_CACHE_MAX_BYTES=${ODYSSEUS_TTS_CACHE_MAX_BYTES}
|
|
- DATA_BRAVE_API_KEY=${DATA_BRAVE_API_KEY:-}
|
|
- GOOGLE_API_KEY=${GOOGLE_API_KEY:-}
|
|
- GOOGLE_PSE_CX=${GOOGLE_PSE_CX:-}
|
|
- GOOGLE_OAUTH_CLIENT_ID=${GOOGLE_OAUTH_CLIENT_ID:-}
|
|
- GOOGLE_OAUTH_CLIENT_SECRET=${GOOGLE_OAUTH_CLIENT_SECRET:-}
|
|
- GOOGLE_OAUTH_REDIRECT_URI=${GOOGLE_OAUTH_REDIRECT_URI:-}
|
|
- 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
|
|
# Advisory: a settings file the migration cannot parse or rewrite must
|
|
# not be what stops searxng from booting. It explains itself on stderr
|
|
# and we carry on, letting searxng report anything genuinely wrong.
|
|
/usr/local/searxng/.venv/bin/python /tmp/migrate-searxng-settings.py /etc/searxng/settings.yml || true
|
|
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
|
|
- ./scripts/migrate_searxng_settings.py:/tmp/migrate-searxng-settings.py: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:
|