2026-09-14 18:53:50 +02:00
|
|
|
# Production backend image: API, both worker roles, and the migration/admin CLI.
|
|
|
|
|
#
|
|
|
|
|
# One image, several commands (see docker/entrypoint.sh) — matches SPEC §6.3's
|
|
|
|
|
# WORKER_ROLE split. Build context is the REPO ROOT, because the image needs
|
2026-09-16 22:14:43 +02:00
|
|
|
# backend/ *and* plugins/ (the plugins are separate distributions that the
|
2026-09-14 18:53:50 +02:00
|
|
|
# loader discovers through the `webmail.plugins` entry-point group, SPEC §11).
|
|
|
|
|
#
|
|
|
|
|
# docker build -f docker/Dockerfile.backend -t webmail/backend .
|
|
|
|
|
|
|
|
|
|
# ---------- builder: resolve wheels into a self-contained venv ----------------
|
|
|
|
|
FROM python:3.11-slim AS builder
|
|
|
|
|
|
|
|
|
|
ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \
|
|
|
|
|
PIP_NO_CACHE_DIR=1 \
|
|
|
|
|
PYTHONDONTWRITEBYTECODE=1
|
|
|
|
|
|
|
|
|
|
# Only needed if a pinned dep has no manylinux wheel for this platform; the
|
|
|
|
|
# whole stage is discarded, so it costs nothing in the runtime image.
|
|
|
|
|
RUN apt-get update \
|
|
|
|
|
&& apt-get install -y --no-install-recommends build-essential \
|
|
|
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
|
|
|
|
|
|
RUN python -m venv /opt/venv
|
|
|
|
|
ENV PATH="/opt/venv/bin:$PATH"
|
|
|
|
|
|
|
|
|
|
# Backend first: its dependency set is the slowest layer and changes least often.
|
|
|
|
|
COPY backend/pyproject.toml /src/backend/pyproject.toml
|
|
|
|
|
COPY backend/app /src/backend/app
|
|
|
|
|
COPY backend/workers /src/backend/workers
|
|
|
|
|
RUN pip install /src/backend
|
|
|
|
|
|
|
|
|
|
# Plugins are ordinary distributions installed on top (each owns its own deps).
|
|
|
|
|
COPY plugins /src/plugins
|
|
|
|
|
RUN pip install \
|
|
|
|
|
/src/plugins/webmail_plugin_calendar \
|
|
|
|
|
/src/plugins/webmail_plugin_contacts \
|
|
|
|
|
/src/plugins/webmail_plugin_labels \
|
2026-09-16 22:14:43 +02:00
|
|
|
/src/plugins/webmail_plugin_filters \
|
|
|
|
|
/src/plugins/webmail_plugin_antispam
|
2026-09-14 18:53:50 +02:00
|
|
|
|
|
|
|
|
# ---------- runtime -----------------------------------------------------------
|
|
|
|
|
FROM python:3.11-slim
|
|
|
|
|
|
|
|
|
|
ARG APP_VERSION=dev
|
|
|
|
|
ARG GIT_SHA=unknown
|
|
|
|
|
LABEL org.opencontainers.image.title="webmail-backend" \
|
|
|
|
|
org.opencontainers.image.version="${APP_VERSION}" \
|
|
|
|
|
org.opencontainers.image.revision="${GIT_SHA}"
|
|
|
|
|
|
|
|
|
|
ENV PYTHONUNBUFFERED=1 \
|
|
|
|
|
PYTHONDONTWRITEBYTECODE=1 \
|
|
|
|
|
PATH="/opt/venv/bin:$PATH" \
|
|
|
|
|
APP_VERSION="${APP_VERSION}" \
|
|
|
|
|
GIT_SHA="${GIT_SHA}"
|
|
|
|
|
|
|
|
|
|
# tini reaps zombies and forwards SIGTERM, which the workers need for the <10 s
|
|
|
|
|
# graceful lease release in workers/run.py.
|
|
|
|
|
RUN apt-get update \
|
|
|
|
|
&& apt-get install -y --no-install-recommends tini \
|
|
|
|
|
&& rm -rf /var/lib/apt/lists/* \
|
|
|
|
|
&& useradd --system --uid 10001 --create-home --home-dir /home/webmail webmail
|
|
|
|
|
|
|
|
|
|
COPY --from=builder /opt/venv /opt/venv
|
|
|
|
|
|
|
|
|
|
WORKDIR /app
|
|
|
|
|
# Alembic's core tree lives outside the installed package, so it is copied in.
|
|
|
|
|
COPY backend/alembic.ini ./alembic.ini
|
|
|
|
|
COPY backend/migrations ./migrations
|
|
|
|
|
COPY backend/scripts ./scripts
|
|
|
|
|
COPY docker/entrypoint.sh /usr/local/bin/entrypoint
|
|
|
|
|
RUN chmod +x /usr/local/bin/entrypoint
|
|
|
|
|
|
|
|
|
|
# Blob store mount point (BLOB_ROOT). Ownership is set by the compose `init`
|
|
|
|
|
# service, which runs before anything else touches the bind mount.
|
|
|
|
|
RUN mkdir -p /data/blobs && chown webmail:webmail /data/blobs
|
|
|
|
|
VOLUME ["/data/blobs"]
|
|
|
|
|
|
|
|
|
|
USER webmail
|
|
|
|
|
ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/entrypoint"]
|
|
|
|
|
CMD ["api"]
|