95 lines
3.6 KiB
Docker
95 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 /mnt/static /mnt/media \
|
|
&& chown -R mnemosyne:mnemosyne /app /mnt/static /mnt/media
|
|
USER mnemosyne
|
|
|
|
# The compose file overrides this per service. Default = Django web.
|
|
ENTRYPOINT ["entrypoint.sh"]
|
|
CMD ["web"]
|