feat(docker): rename web service to app, add nginx as web
All checks were successful
CVE Scan & Docker Build / security-scan (push) Successful in 53s
CVE Scan & Docker Build / build-and-push (push) Successful in 3m0s

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
This commit is contained in:
2026-05-03 19:35:27 -04:00
parent a2c885cf34
commit 7185d326eb
10 changed files with 163 additions and 38 deletions

View File

@@ -2,20 +2,20 @@
# Mnemosyne — production deployment
# =============================================================================
# Four services, all from the same image:
# web — Django REST API + admin (gunicorn, port 8000)
# app — Django REST API + admin (gunicorn, port 8000)
# mcp — FastMCP server (uvicorn, port 22091)
# worker — Celery worker (embedding/ingest/batch queues)
# nginx — reverse proxy, public port 23090
# web — reverse proxy, public port 23090 (nginx)
#
# External services (NOT spun up here): Postgres on Portia, Neo4j on Ariel,
# External services (NOT spun up here): Postgres on Portia, Neo4j on Umbriel,
# RabbitMQ on Oberon, S3/MinIO on Nyx, Memcached on its own host, embedder
# and reranker on Nyx, smtp4dev on Oberon. All reached over the internal
# 10.10.0.0/24 network.
#
# Run:
# docker compose up -d
# docker compose run --rm web migrate # one-shot DB migrate
# docker compose run --rm web setup # Neo4j indexes + library types
# docker compose run --rm app migrate # one-shot DB migrate
# docker compose run --rm app setup # Neo4j indexes + library types
# =============================================================================
services:
@@ -31,8 +31,8 @@ services:
- mnemosyne-static:/shared-static
restart: "no"
# ── Web app: Django REST API + admin ───────────────────────────────────────
web:
# ── App: Django REST API + admin ──────────────────────────────────────────
app:
image: git.helu.ca/r/mnemosyne:latest
command: ["web"]
env_file: mnemosyne/.env
@@ -45,7 +45,7 @@ services:
expose:
- "8000"
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/admin/login/').read()"]
test: ["CMD", "curl", "-f", "http://localhost:8000/live/"]
interval: 30s
timeout: 5s
retries: 3
@@ -62,7 +62,7 @@ services:
expose:
- "22091"
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:22091/mcp/health').read()"]
test: ["CMD", "curl", "-f", "http://localhost:22091/mcp/health"]
interval: 30s
timeout: 5s
retries: 3
@@ -74,6 +74,9 @@ services:
command: ["worker"]
env_file: mnemosyne/.env
restart: unless-stopped
depends_on:
app:
condition: service_healthy
volumes:
- mnemosyne-media:/app/media
healthcheck:
@@ -83,26 +86,28 @@ services:
retries: 3
start_period: 60s
# ── nginx: reverse proxy, public port 23090 ────────────────────────────────
nginx:
# ── Web: nginx reverse proxy, public port 23090 ───────────────────────────
web:
image: nginx:alpine
restart: unless-stopped
depends_on:
- web
- mcp
app:
condition: service_healthy
mcp:
condition: service_healthy
ports:
- "23090:80"
volumes:
- ./nginx/mnemosyne.conf:/etc/nginx/conf.d/default.conf:ro
- mnemosyne-static:/var/www/static:ro
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost/healthz"]
test: ["CMD", "curl", "-f", "http://localhost/live/"]
interval: 30s
timeout: 5s
retries: 3
volumes:
# Static files baked into the image at /app/staticfiles. The web service
# Static files baked into the image at /app/staticfiles. The app service
# mounts this volume, populating it on first start; nginx reads from it.
mnemosyne-static:
# Local FileSystemStorage fallback. Production uses USE_LOCAL_STORAGE=False