mirror of
https://github.com/chatmail/relay.git
synced 2026-05-10 16:04:37 +00:00
Add container-based deployment as an alternative to bare-metal pyinfra. - systemd inside container reusing the existing deployer infrastructure - chatmail-init.sh runs `cmdeploy run --ssh-host @local` on first boot, so the container self-deploys using the same code path as bare-metal - Config via MAIL_DOMAIN env var (simple) or mounted chatmail.ini (advanced) - External TLS support via TLS_EXTERNAL_CERT_AND_KEY for reverse proxy setups - Image version tracking in /etc/chatmail-image-version for upgrade detection - .git/ excluded, but version file mocked so git revparse still works - Health check verifies postfix, dovecot, and nginx are listening Files added: - docker/chatmail_relay.dockerfile: multi-stage build (build + runtime) - docker/chatmail-init.sh: first-boot deployment script - docker/chatmail-init.service: systemd unit for init script - docker/entrypoint.sh: container entrypoint (starts systemd) - docker/healthcheck.sh: container health check - docker/docker-compose.yaml: main compose config - docker/docker-compose.ci.yaml: CI override (uses GHCR image) - docker/docker-compose.override.yaml.example: customization template - docker/build.sh: helper script - doc/source/docker.rst: documentation - .dockerignore: build context filter
111 lines
4.3 KiB
Docker
111 lines
4.3 KiB
Docker
# syntax=docker/dockerfile:1
|
|
FROM jrei/systemd-debian:12 AS base
|
|
|
|
ENV LANG=en_US.UTF-8
|
|
|
|
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
|
--mount=type=cache,target=/var/lib/apt/lists,sharing=locked \
|
|
echo 'APT::Install-Recommends "0";' > /etc/apt/apt.conf.d/01norecommend && \
|
|
echo 'APT::Install-Suggests "0";' >> /etc/apt/apt.conf.d/01norecommend && \
|
|
apt-get update && \
|
|
DEBIAN_FRONTEND=noninteractive TZ=UTC \
|
|
apt-get install -y \
|
|
ca-certificates \
|
|
gcc \
|
|
git \
|
|
python3 \
|
|
python3-dev \
|
|
python3-venv \
|
|
tzdata \
|
|
locales && \
|
|
sed -i -e "s/# $LANG.*/$LANG UTF-8/" /etc/locale.gen && \
|
|
dpkg-reconfigure --frontend=noninteractive locales && \
|
|
update-locale LANG=$LANG
|
|
|
|
# --- Build-time: install cmdeploy venv and run install stage ---
|
|
# Editable install so importlib.resources reads directly from the source tree.
|
|
# On container start only "configure,activate" stages run.
|
|
|
|
# Copy dependency metadata first so pip install layer is cached
|
|
COPY cmdeploy/pyproject.toml /opt/chatmail/cmdeploy/pyproject.toml
|
|
COPY chatmaild/pyproject.toml /opt/chatmail/chatmaild/pyproject.toml
|
|
|
|
# Dummy scaffolding so editable install can discover packages
|
|
RUN mkdir -p /opt/chatmail/cmdeploy/src/cmdeploy \
|
|
/opt/chatmail/chatmaild/src/chatmaild && \
|
|
touch /opt/chatmail/cmdeploy/src/cmdeploy/__init__.py \
|
|
/opt/chatmail/chatmaild/src/chatmaild/__init__.py
|
|
|
|
# Dummy git repo: .git/ is excluded from the build context (.dockerignore)
|
|
# but setuptools calls `git ls-files` when building the sdist.
|
|
WORKDIR /opt/chatmail
|
|
RUN --mount=type=cache,target=/root/.cache/pip \
|
|
git init -q && \
|
|
python3 -m venv /opt/cmdeploy && \
|
|
/opt/cmdeploy/bin/pip install -e chatmaild/ -e cmdeploy/
|
|
|
|
# Full source copy (editable install's .egg-link still points here)
|
|
COPY . /opt/chatmail/
|
|
|
|
# Minimal chatmail.ini
|
|
RUN printf '[params]\nmail_domain = build.local\n' > /tmp/chatmail.ini
|
|
|
|
RUN CMDEPLOY_STAGES=install \
|
|
CHATMAIL_INI=/tmp/chatmail.ini \
|
|
CHATMAIL_NOSYSCTL=True \
|
|
CHATMAIL_NOPORTCHECK=True \
|
|
/opt/cmdeploy/bin/pyinfra @local \
|
|
/opt/chatmail/cmdeploy/src/cmdeploy/run.py -y
|
|
|
|
RUN cp -a www/ /opt/chatmail-www/
|
|
|
|
# Remove build-only packages — not needed at runtime.
|
|
# Keep git: test_deployed_state needs `git rev-parse HEAD` to verify the
|
|
# deployed version hash matches /etc/chatmail-version.
|
|
RUN apt-get purge -y gcc python3-dev && \
|
|
apt-get autoremove -y && \
|
|
rm -f /tmp/chatmail.ini
|
|
|
|
# Record image version (used in deploy fingerprint at runtime).
|
|
# GIT_HASH is passed as a build arg (from docker-compose or CI) so that
|
|
# .git/ can be excluded from the build context via .dockerignore.
|
|
# Two files: chatmail-image-version is the immutable build hash (survives
|
|
# deploys); chatmail-version is overwritten by cmdeploy run and restored
|
|
# from the image version after each deploy in chatmail-init.sh.
|
|
ARG GIT_HASH=unknown
|
|
RUN echo "$GIT_HASH" > /etc/chatmail-image-version && \
|
|
echo "$GIT_HASH" > /etc/chatmail-version
|
|
|
|
# Mock git HEAD so `git rev-parse HEAD` returns the source repo's commit hash.
|
|
# The .git/ dir was created by `git init` earlier (for setuptools); we just
|
|
# write the build hash into whatever branch HEAD points to.
|
|
RUN head_ref=$(sed 's/^ref: //' /opt/chatmail/.git/HEAD) && \
|
|
mkdir -p "/opt/chatmail/.git/$(dirname "$head_ref")" && \
|
|
echo "$GIT_HASH" > "/opt/chatmail/.git/$head_ref"
|
|
# --- End build-time install ---
|
|
|
|
ENV TZ=:/etc/localtime
|
|
ENV PATH="/opt/cmdeploy/bin:${PATH}"
|
|
RUN ln -s /etc/chatmail/chatmail.ini /opt/chatmail/chatmail.ini
|
|
|
|
ARG CHATMAIL_INIT_SERVICE_PATH=/lib/systemd/system/chatmail-init.service
|
|
COPY ./docker/chatmail-init.service "$CHATMAIL_INIT_SERVICE_PATH"
|
|
RUN ln -sf "$CHATMAIL_INIT_SERVICE_PATH" "/etc/systemd/system/multi-user.target.wants/chatmail-init.service"
|
|
|
|
# Remove default nginx site config at build time (not in entrypoint)
|
|
RUN rm -f /etc/nginx/sites-enabled/default
|
|
|
|
COPY --chmod=555 ./docker/chatmail-init.sh /chatmail-init.sh
|
|
COPY --chmod=555 ./docker/entrypoint.sh /entrypoint.sh
|
|
COPY --chmod=555 ./docker/healthcheck.sh /healthcheck.sh
|
|
|
|
HEALTHCHECK --interval=10s --start-period=180s --timeout=10s --retries=3 \
|
|
CMD /healthcheck.sh
|
|
|
|
STOPSIGNAL SIGRTMIN+3
|
|
|
|
ENTRYPOINT ["/entrypoint.sh"]
|
|
|
|
CMD [ "--default-standard-output=journal+console", \
|
|
"--default-standard-error=journal+console" ]
|