From 2e23fadb5484d90b7def5e1305a005c7ebf493aa Mon Sep 17 00:00:00 2001 From: j4n Date: Tue, 17 Feb 2026 15:09:48 +0100 Subject: [PATCH] docker: skip redundant cmdeploy run on container restart Replace the old IMAGE_VERSION_FILE/RUNNING_VERSION_FILE mechanism with a single deploy fingerprint (image_version:sha256(chatmail.ini)) stored at /etc/chatmail/.deploy-fingerprint. On restart, if the fingerprint matches the last successful deploy, skip cmdeploy run entirely. The fingerprint lives on the container's writable layer. On fresh containers, setting CMDEPLOY_STAGES non-empty in env forces a deploy run regardless of fingerprint. Also narrow the /home volume mount to /home/vmail. --- docker-compose.yaml | 4 +- docker/chatmail_relay.dockerfile | 4 +- docker/docker-compose.override.yaml.example | 2 +- docker/files/setup_chatmail_docker.sh | 53 ++++++++++----------- 4 files changed, 30 insertions(+), 33 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 226fd327..fc3554ea 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -41,11 +41,11 @@ services: ## system (required) - /sys/fs/cgroup:/sys/fs/cgroup:rw ## data (defaults — override in docker-compose.override.yaml) - - chatmail-mail:/home + - chatmail-data:/home/vmail - chatmail-dkimkeys:/etc/dkimkeys - chatmail-acme:/var/lib/acme volumes: - chatmail-mail: + chatmail-data: chatmail-dkimkeys: chatmail-acme: diff --git a/docker/chatmail_relay.dockerfile b/docker/chatmail_relay.dockerfile index bdbcf65c..f54d76db 100644 --- a/docker/chatmail_relay.dockerfile +++ b/docker/chatmail_relay.dockerfile @@ -49,7 +49,7 @@ RUN printf '[params]\nmail_domain = build.local\n' > /tmp/chatmail.ini # Dummy git repo init: .git/ is excluded from the build context (.dockerignore) # but setuptools calls `git ls-files` when building the sdist. -RUN git init && \ +RUN git init -q && \ python3 -m venv /opt/cmdeploy && \ /opt/cmdeploy/bin/pip install --no-cache-dir \ -e chatmaild/ -e cmdeploy/ @@ -65,7 +65,7 @@ RUN cp -a www/ /opt/chatmail-www/ RUN rm -f /tmp/chatmail.ini -# Record image version for upgrade detection at runtime. +# 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. ARG GIT_HASH=unknown diff --git a/docker/docker-compose.override.yaml.example b/docker/docker-compose.override.yaml.example index e6ebfd4a..6503dc3b 100644 --- a/docker/docker-compose.override.yaml.example +++ b/docker/docker-compose.override.yaml.example @@ -11,7 +11,7 @@ services: ## Data paths — bind-mount to host directories for easy access/backup. ## Uncomment and adjust paths as needed. These override the named ## volumes in the base docker-compose.yaml. - # - ./data/chatmail:/home + # - ./data/chatmail:/home/vmail # - ./data/chatmail-dkimkeys:/etc/dkimkeys # - ./data/chatmail-acme:/var/lib/acme diff --git a/docker/files/setup_chatmail_docker.sh b/docker/files/setup_chatmail_docker.sh index 69ec6542..a1f9c82a 100755 --- a/docker/files/setup_chatmail_docker.sh +++ b/docker/files/setup_chatmail_docker.sh @@ -18,40 +18,37 @@ fi # Fix ownership for bind-mounted keys (host opendkim UID may differ from container) chown -R opendkim:opendkim /etc/dkimkeys +# Journald: forward to console for docker logs +grep -q '^ForwardToConsole=yes' /etc/systemd/journald.conf \ + || echo "ForwardToConsole=yes" >> /etc/systemd/journald.conf +systemctl restart systemd-journald + # Create chatmail.ini (skips if file already exists, e.g. volume-mounted) mkdir -p "$(dirname "$CHATMAIL_INI")" if [ ! -f "$CHATMAIL_INI" ]; then $CMDEPLOY init --config "$CHATMAIL_INI" "$MAIL_DOMAIN" fi -# Auto-detect image upgrades: if the image version changed since last run, -# include the install stage so new packages/binaries are picked up. +# --- Deploy fingerprint: skip cmdeploy run if nothing changed --- +# On restart with identical image+config, systemd already brings up all +# enabled services — the full cmdeploy run is redundant (~30s saved). +# The install stage runs at image build time (Dockerfile), so only +# configure+activate are needed here. IMAGE_VERSION_FILE="/etc/chatmail-image-version" -RUNNING_VERSION_FILE="/home/.chatmail-running-version" -CMDEPLOY_STAGES="${CMDEPLOY_STAGES:-configure,activate}" -if [ -f "$IMAGE_VERSION_FILE" ]; then - image_ver=$(cat "$IMAGE_VERSION_FILE") - running_ver="" - if [ -f "$RUNNING_VERSION_FILE" ]; then - running_ver=$(cat "$RUNNING_VERSION_FILE") - fi - if [ "$image_ver" != "$running_ver" ]; then - echo "[INFO] Image version changed ($running_ver -> $image_ver), adding install stage." - case "$CMDEPLOY_STAGES" in - *install*) ;; # already includes install - *) CMDEPLOY_STAGES="install,$CMDEPLOY_STAGES" ;; - esac - fi -fi -export CMDEPLOY_STAGES -$CMDEPLOY run --config "$CHATMAIL_INI" --ssh-host @local +FINGERPRINT_FILE="/etc/chatmail/.deploy-fingerprint" +image_ver="none" +[ -f "$IMAGE_VERSION_FILE" ] && image_ver=$(cat "$IMAGE_VERSION_FILE") +config_hash=$(sha256sum "$CHATMAIL_INI" | cut -c1-16) +current_fp="${image_ver}:${config_hash}" -# Record successful version after deploy -if [ -f "$IMAGE_VERSION_FILE" ]; then - cp "$IMAGE_VERSION_FILE" "$RUNNING_VERSION_FILE" +# CMDEPLOY_STAGES non-empty in env = operator override → always run. +# Otherwise, if fingerprint matches the last successful deploy, skip. +if [ -z "${CMDEPLOY_STAGES:-}" ] \ + && [ -f "$FINGERPRINT_FILE" ] \ + && [ "$(cat "$FINGERPRINT_FILE")" = "$current_fp" ]; then + echo "[INFO] No changes detected ($current_fp), skipping deploy." +else + export CMDEPLOY_STAGES="${CMDEPLOY_STAGES:-configure,activate}" + $CMDEPLOY run --config "$CHATMAIL_INI" --ssh-host @local + echo "$current_fp" > "$FINGERPRINT_FILE" fi - -# Journald: forward to console for docker logs (idempotent) -grep -q '^ForwardToConsole=yes' /etc/systemd/journald.conf \ - || echo "ForwardToConsole=yes" >> /etc/systemd/journald.conf -systemctl restart systemd-journald