#!/bin/sh # Mnemosyne container entrypoint. # # The same image runs all three processes — the compose service supplies # `web`, `mcp`, `worker`, or `migrate` as CMD. set -e case "$1" in web) # Django REST API + admin (gunicorn → wsgi). exec gunicorn \ --config /app/docker/gunicorn.conf.py \ --bind 0.0.0.0:8000 \ --workers "${GUNICORN_WORKERS:-3}" \ --access-logfile - \ --error-logfile - \ mnemosyne.wsgi:application ;; mcp) # FastMCP over Streamable HTTP at /mcp/, mounted by mnemosyne.asgi. exec uvicorn \ --host 0.0.0.0 \ --port 8001 \ --workers "${UVICORN_WORKERS:-1}" \ mnemosyne.asgi:app ;; worker) # Celery worker covering embedding + ingest + batch + default queues. # In production you may want to split these onto separate worker # services for queue-level isolation; one process is fine to start. exec celery -A mnemosyne worker \ --loglevel="${CELERY_LOG_LEVEL:-info}" \ --queues="${CELERY_QUEUES:-celery,embedding,batch}" \ --concurrency="${CELERY_CONCURRENCY:-2}" ;; beat) # Celery scheduled tasks (only needed if/when periodic jobs are wired). exec celery -A mnemosyne beat \ --loglevel="${CELERY_LOG_LEVEL:-info}" ;; migrate) # One-shot DB migration runner — invoke before bringing services up # for the first time or after a deploy. exec python manage.py migrate --noinput ;; setup) # One-shot init — Neo4j indexes + library_type seed data. Run this # manually after the system embedding model has been configured in the # admin (setup_neo4j_indexes reads vector dimensions from that row). python manage.py setup_neo4j_indexes python manage.py load_library_types ;; init) # Bundled one-shot init run by the `init` sidecar on every # `docker compose up`. Idempotent: re-runs are no-ops unless migrations # or library_type defaults need to change. A non-zero exit here blocks # `app`, `mcp`, and `worker` from starting. # # collectstatic copies the static files baked into the image at build # time (/app/staticfiles) into STATIC_ROOT (/mnt/static), which nginx # serves directly. --clear removes any stale files from the previous # deploy before copying, so deleted assets don't linger. # # Neo4j vector-index creation is *deliberately not* bundled here. That # command (``setup_neo4j_indexes``) requires a system embedding model # with a configured ``vector_dimensions`` value, and that model is # data an operator configures through the Django admin after first # boot. On a fresh stack there is no such row yet, so blocking the # whole stack on it would make the admin unreachable — a chicken-and- # egg. Operator bootstrap flow: # # 1. docker compose up # init sidecar: migrate + collectstatic + load_library_types # 2. browse to admin, configure system embedding model # 3. docker compose exec app python manage.py setup_neo4j_indexes # # Until step 3 runs, vector search will return empty results — the # readiness check in library/apps.py logs a warning when indexes are # missing so this is visible, not silent. set -e python manage.py migrate --noinput python manage.py collectstatic --noinput --clear python manage.py load_library_types ;; shell) # Drop into the management shell for ad-hoc work. exec python manage.py shell ;; *) # Fall through: run whatever was passed (e.g. `manage.py `). exec "$@" ;; esac