Remove dedicated static-init service and run collectstatic in the init sidecar instead.
Static files baked into the image are copied to /mnt/static for nginx serving on each
deployment. Also update MCP and nginx ports and refresh external service hostnames
in comments.
Rework README and docker-compose comments to document the deliberate
chicken-and-egg escape: the `init` sidecar now only runs `migrate` and
`load_library_types`, leaving `setup_neo4j_indexes` as a manual step
after the system embedding model is configured in `/admin/`. This
avoids making `app` unreachable on first boot when no embedding model
row exists yet, while preserving loud failure on dimension mismatch.
Introduces a one-shot `init` service in docker-compose that runs Postgres
migrations, Neo4j index setup, and library-type seeding on every `up`.
Long-running services (`app`, `mcp`, `worker`) now depend on its
successful completion via `service_completed_successfully`, blocking the
stack on configuration errors (missing embedding model, dimension
mismatch, unreachable DB) rather than serving silent zero-result
searches.
Also standardizes reranker test fixtures to use the `/v1` OpenAI-style
base URL convention used across other service clients.
Reorganize Docker Compose services: the Django/gunicorn container is now
`app` and nginx is `web`, better reflecting their roles. Add a dedicated
gunicorn configuration and install curl in the runtime image for health
checks.
Update documentation to reflect:
- Neo4j migration from ariel.incus to a dedicated umbriel.incus instance
- Rationale for requiring a dedicated Neo4j instance (single-tenancy
assumptions, label/index isolation, schema ownership)
- New service naming in compose commands and log tailing examples
Adds a complete deployment surface for production:
Dockerfile multi-stage 3.12-slim build, collectstatic
baked into the image, runs as non-root mnemosyne
uid/gid 1000.
docker/entrypoint.sh dispatches `web | mcp | worker | beat | migrate
| setup | shell` from a single image, so every
service in compose runs the same artifact.
docker-compose.yaml five services: static-init (one-shot copies
statics into the shared volume on every up),
web (gunicorn), mcp (uvicorn), worker (celery),
nginx. External services (Postgres, Neo4j,
RabbitMQ, S3, Memcached, embedder, reranker)
reached over the 10.10.0.0/24 internal network
and configured via mnemosyne/.env.
nginx/mnemosyne.conf reverse proxy: /library/* and /admin/* → web,
/mcp/* → mcp, /static/* → volume, /metrics
internal-network-only (127/8 + RFC1918), /healthz
proxies to /mcp/health for liveness probes.
.gitea/workflows/ CVE scan + image build, image pushed to
git.helu.ca/r/mnemosyne. Trivy scans pyproject
extras (dev/test/lint/docs) and the built image.
pyproject.toml adds [test], [lint], [docs] extras so the CI
pip-compile step has something to resolve.
README documents the bring-up flow (`docker compose run --rm web migrate`,
then `setup`, then `up -d`), day-to-day commands, and the env-var values
that need adjusting for production (DEBUG=False, KVDB_LOCATION pointing
at the external memcached, AWS keys filled in, etc.).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>