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
128 lines
4.6 KiB
Plaintext
128 lines
4.6 KiB
Plaintext
# Mnemosyne nginx — single virtual host that fronts the Django web app
|
||
# and the FastMCP server. HAProxy on Titania terminates TLS and routes by
|
||
# hostname; this nginx is plain HTTP on the internal network.
|
||
|
||
# Suppress probe paths from the access log (health checks, Prometheus scrapes).
|
||
# These fire every 15–30 s and would drown out real traffic in Loki.
|
||
map $request_uri $loggable {
|
||
default 1;
|
||
~^/live(/|\?|$) 0;
|
||
~^/ready(/|\?|$) 0;
|
||
~^/metrics(/|\?|$) 0;
|
||
~^/healthz(/|\?|$) 0;
|
||
~^/health 0;
|
||
~^/mcp/health(/|\?|$) 0;
|
||
~^/ping(/|\?|$) 0;
|
||
}
|
||
|
||
# Map of upstreams to give us readable proxy_pass targets and easy retries.
|
||
upstream mnemosyne_app {
|
||
server app:8000 max_fails=3 fail_timeout=30s;
|
||
}
|
||
|
||
upstream mnemosyne_mcp {
|
||
server mcp:22091 max_fails=3 fail_timeout=30s;
|
||
}
|
||
|
||
server {
|
||
listen 80 default_server;
|
||
server_name _;
|
||
|
||
access_log /var/log/nginx/access.log combined if=$loggable;
|
||
|
||
# Reasonable limits — file uploads to the ingest endpoint can be big,
|
||
# but the bulk path is S3-direct from Daedalus. 64 MB covers admin
|
||
# uploads and direct REST POST /library/api/items/upload.
|
||
client_max_body_size 64m;
|
||
client_body_timeout 120s;
|
||
|
||
# Liveness probe — always 200 if the Django process is up.
|
||
# Use the trailing-slash form: /live/ returns 200 directly.
|
||
# /live (no slash) triggers Django's APPEND_SLASH 301 redirect, which
|
||
# will cause health check clients that don't follow redirects to fail.
|
||
location = /live/ {
|
||
proxy_pass http://mnemosyne_app;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
access_log off;
|
||
}
|
||
|
||
# Readiness probe — 200 only when PostgreSQL + Memcached are reachable.
|
||
# Same trailing-slash rule applies.
|
||
location = /ready/ {
|
||
proxy_pass http://mnemosyne_app;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
access_log off;
|
||
}
|
||
|
||
# HAProxy liveness probe — proxies through to the MCP health endpoint.
|
||
location = /healthz {
|
||
proxy_pass http://mnemosyne_mcp/mcp/health;
|
||
access_log off;
|
||
}
|
||
|
||
# Mnemosyne's REST API — Django REST Framework views + admin.
|
||
# Under /library/api/* per mnemosyne/urls.py and /admin/* per Django.
|
||
location /library/ {
|
||
proxy_pass http://mnemosyne_app;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
proxy_read_timeout 300s;
|
||
}
|
||
|
||
location /admin/ {
|
||
proxy_pass http://mnemosyne_app;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
proxy_read_timeout 300s;
|
||
}
|
||
|
||
# FastMCP Streamable HTTP at /mcp/ and SSE at /mcp/sse/.
|
||
# Long-running streams need disabled buffering and a generous timeout.
|
||
location /mcp/ {
|
||
proxy_pass http://mnemosyne_mcp;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
proxy_set_header Connection "";
|
||
proxy_buffering off;
|
||
proxy_cache off;
|
||
proxy_read_timeout 600s;
|
||
}
|
||
|
||
# Static files baked into the image at /app/staticfiles, mounted into
|
||
# this nginx via a named volume populated by the app service.
|
||
location /static/ {
|
||
alias /var/www/static/;
|
||
access_log off;
|
||
expires 30d;
|
||
}
|
||
|
||
# Prometheus scrape endpoint — internal networks only.
|
||
# Allows: loopback + all RFC1918 private ranges.
|
||
location /metrics {
|
||
allow 127.0.0.0/8;
|
||
allow 10.0.0.0/8;
|
||
allow 172.16.0.0/12;
|
||
allow 192.168.0.0/16;
|
||
deny all;
|
||
proxy_pass http://mnemosyne_app;
|
||
access_log off;
|
||
}
|
||
}
|