Files
mnemosyne/Dockerfile
Robert Helewka 7185d326eb
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
feat(docker): rename web service to app, add nginx as web
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
2026-05-03 19:35:27 -04:00

96 lines
3.6 KiB
Docker

# =============================================================================
# Mnemosyne — production image
# =============================================================================
# Multi-stage:
# builder installs Python deps and runs `collectstatic` once.
# runtime copies only the artifacts the running process needs.
#
# The same image runs three different processes (Django web, MCP server,
# Celery worker) — the compose file picks the command per service.
# =============================================================================
# ── Stage 1: builder ────────────────────────────────────────────────────────
FROM python:3.12-slim AS builder
# Build deps for psycopg, PyMuPDF, Pillow, cryptography, etc.
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
libpq-dev \
libffi-dev \
libssl-dev \
libjpeg-dev \
zlib1g-dev \
&& rm -rf /var/lib/apt/lists/*
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
WORKDIR /build
# Install dependencies first (better layer caching).
COPY pyproject.toml README.md ./
COPY mnemosyne/ ./mnemosyne/
RUN pip install --upgrade pip \
&& pip install .
# Bake static files into the image. The env vars below are build-time-only
# stubs needed for settings.py to import without real infrastructure — they
# never reach the runtime image because this is the builder stage.
# Inlined into the RUN command (rather than ENV/ARG) so static analysis
# tools (Trivy) don't flag them as baked-in secrets.
ENV DJANGO_SETTINGS_MODULE=mnemosyne.settings \
DEBUG=False \
USE_LOCAL_STORAGE=True \
APP_DB_NAME=collectstatic \
APP_DB_USER=collectstatic
WORKDIR /build/mnemosyne
RUN SECRET_KEY=collectstatic-stub \
APP_DB_PASSWORD=collectstatic-stub \
python manage.py collectstatic --noinput --clear
# ── Stage 2: runtime ────────────────────────────────────────────────────────
FROM python:3.12-slim AS runtime
# Runtime libs for psycopg + PyMuPDF + Pillow + cryptography.
RUN apt-get update && apt-get install -y --no-install-recommends \
libpq5 \
libjpeg62-turbo \
zlib1g \
libssl3 \
ca-certificates \
curl \
&& rm -rf /var/lib/apt/lists/*
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
DJANGO_SETTINGS_MODULE=mnemosyne.settings \
PATH=/usr/local/bin:$PATH
# Copy installed packages from the builder.
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
# Application code + collected statics.
WORKDIR /app
COPY --from=builder /build/mnemosyne /app
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
COPY docker/gunicorn.conf.py /app/docker/gunicorn.conf.py
RUN chmod +x /usr/local/bin/entrypoint.sh
# Non-root user for everything that runs in this image. uid:gid 1000:1000
# matches the convention for a single-application container.
RUN groupadd --gid 1000 mnemosyne \
&& useradd --uid 1000 --gid mnemosyne --home /app --no-create-home --shell /sbin/nologin mnemosyne \
&& mkdir -p /app/media /app/logs \
&& chown -R mnemosyne:mnemosyne /app
USER mnemosyne
# The compose file overrides this per service. Default = Django web.
EXPOSE 8000 22091
ENTRYPOINT ["entrypoint.sh"]
CMD ["web"]