docker: BuildKit cache + CGO LTO optimization

Add persistent cache mounts for the Go module cache, Go build cache,
and apt so only changed packages are re-downloaded or recompiled on
subsequent builds.

CGO LTO is injected via `make LIBFIDO2_LDFLAGS=...` rather than ENV
because the Makefile sets CGO_LDFLAGS inline in go-build-finalize,
clobbering any inherited environment variable. Binary stripping is done
with strip --strip-all post-build since the Makefile owns the -ldflags
chain and cannot be extended without losing the -X version constants.
This commit is contained in:
c41ms0n 2026-04-23 00:36:06 +03:00
parent ba65344ec1
commit dad7066244
2 changed files with 106 additions and 13 deletions

View file

@ -1,21 +1,104 @@
# syntax=docker/dockerfile:1
### The Deb install is just a repack of the official ProtonMail Bridge deb package with less dependencies. ### The Deb install is just a repack of the official ProtonMail Bridge deb package with less dependencies.
### I recommend you don't use this. It's here for legacy reasons. ### I recommend you don't use this. It's here for legacy reasons.
# =============================================================================
# BUILD STAGE
# =============================================================================
FROM golang:1.26-trixie AS build FROM golang:1.26-trixie AS build
ARG version ARG version
ENV version=${version} ENV version=${version}
# ---------------------------------------------------------------------------
# LTO — why ENV CGO_LDFLAGS didn't work:
#
# The Makefile's go-build-finalize on Linux expands to:
# CGO_LDFLAGS="${LIBFIDO2_LDFLAGS}" go build ...
# That inline shell assignment *overwrites* any CGO_LDFLAGS inherited from
# the Docker environment entirely.
#
# Fix: pass LIBFIDO2_LDFLAGS on the make command line (see RUN step below).
# make CLI variables override Makefile ?= and := definitions, so our flags
# reach the CGO_LDFLAGS inline assignment verbatim.
#
# To disable LTO at build time:
# docker buildx build --build-arg CGO_LTO_FLAGS="" ...
# ---------------------------------------------------------------------------
ARG CGO_LTO_FLAGS="-flto=auto -O2"
RUN apt-get update && apt-get install -y build-essential libsecret-1-dev libfido2-dev libcbor-dev # -trimpath: the Makefile never sets this flag, so GOFLAGS is safe here.
# Strips host machine paths from the binary → reproducible builds, no path leaks.
ENV GOFLAGS="-trimpath"
# Build # ---------------------------------------------------------------------------
# System dependencies
# ---------------------------------------------------------------------------
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential \
libsecret-1-dev \
libfido2-dev \
libcbor-dev
# ---------------------------------------------------------------------------
# Fetch source via BuildKit's git ADD (shallow clone at the requested ref).
# Because the entire tree arrives in one shot we cannot pre-copy go.mod,
# but the module cache mount below still avoids re-downloading dependencies.
# ---------------------------------------------------------------------------
ADD https://github.com/ProtonMail/proton-bridge.git#${version} /build/ ADD https://github.com/ProtonMail/proton-bridge.git#${version} /build/
WORKDIR /build/ WORKDIR /build/
RUN make build-nogui vault-editor
# ----------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Pre-seed the Go module cache.
# This layer is invalidated only when go.mod / go.sum change (dep upgrades).
# On pure source changes the cache is warm and this step is near-instant.
# ---------------------------------------------------------------------------
RUN --mount=type=cache,target=/go/pkg/mod,sharing=locked \
go mod download -x
# ---------------------------------------------------------------------------
# Compile.
#
# Cache mounts:
# /go/pkg/mod — downloaded module zips (read, shared)
# /root/.cache/go-build — incremental .a object cache (read-write, locked)
#
# make variable overrides:
# LIBFIDO2_LDFLAGS — appends LTO flags to the CGO_LDFLAGS inline assignment
# in go-build-finalize without touching any other flags.
# Default Makefile value: -lfido2 -lcbor -lssl -lcrypto
#
# Why not -s -w here:
# The Makefile builds its own -ldflags chain:
# BUILD_FLAGS += -ldflags '${GO_LDFLAGS}'
# GO_LDFLAGS carries the -X version/revision constants computed at build
# time. Overriding GO_LDFLAGS on the CLI loses those constants.
# GOFLAGS=-ldflags="-s -w" is silently ignored when the Makefile already
# passes -ldflags on the go build command line (last -ldflags wins in go).
# → We strip the binaries in the next step instead.
# ---------------------------------------------------------------------------
RUN --mount=type=cache,target=/go/pkg/mod,sharing=locked \
--mount=type=cache,target=/root/.cache/go-build,sharing=locked \
make build-nogui vault-editor \
BUILD_ENV=prod \
LIBFIDO2_LDFLAGS="-lfido2 -lcbor -lssl -lcrypto ${CGO_LTO_FLAGS}"
# ---------------------------------------------------------------------------
# Strip debug symbols — equivalent to go -ldflags="-s -w".
# Typically saves 25-35 % on binary size.
# Remove this RUN if you need delve / runtime stack traces.
# ---------------------------------------------------------------------------
RUN strip --strip-all \
/build/proton-bridge \
/build/bridge \
/build/vault-editor
# =============================================================================
# RUNTIME STAGE — minimal Debian image, no build toolchain
# =============================================================================
FROM debian:trixie-slim FROM debian:trixie-slim
LABEL maintainer="Simon Felding <sife@adm.ku.dk>" LABEL maintainer="Simon Felding <sife@adm.ku.dk>"
@ -29,25 +112,31 @@ EXPOSE 143/tcp
WORKDIR /protonmail WORKDIR /protonmail
COPY gpgparams entrypoint.sh /protonmail/ COPY gpgparams entrypoint.sh /protonmail/
# Copy protonmail
COPY --from=build /build/bridge /protonmail/ COPY --from=build /build/bridge /protonmail/
COPY --from=build /build/proton-bridge /protonmail/ COPY --from=build /build/proton-bridge /protonmail/
COPY --from=build /build/vault-editor /protonmail/ COPY --from=build /build/vault-editor /protonmail/
RUN apt-get update \ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update \
&& apt-get install -y --no-install-recommends \ && apt-get install -y --no-install-recommends \
socat pass libsecret-1-0 libfido2-1 ca-certificates procps \ socat pass libsecret-1-0 libfido2-1 ca-certificates procps \
&& case "${PTY_TOOL}" in \ && case "${PTY_TOOL}" in \
dtach) apt-get install -y --no-install-recommends dtach ;; \ dtach) apt-get install -y --no-install-recommends dtach ;; \
abduco) apt-get install -y --no-install-recommends abduco ;; \ abduco) apt-get install -y --no-install-recommends abduco ;; \
reptyr) apt-get install -y --no-install-recommends reptyr ;; \ reptyr) apt-get install -y --no-install-recommends reptyr ;; \
*) echo "Unsupported PTY_TOOL: ${PTY_TOOL}. Supported values are: dtach, abduco, reptyr." >&2; exit 1 ;; \ *) echo "Unsupported PTY_TOOL: ${PTY_TOOL}. Supported: dtach, abduco, reptyr." >&2 ; exit 1 ;; \
esac \ esac \
&& chmod +x /protonmail/entrypoint.sh \ && chmod +x /protonmail/entrypoint.sh \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=30s --timeout=5s --retries=3 --start-period=120s \ HEALTHCHECK --interval=30s --timeout=5s --retries=3 --start-period=120s \
CMD /bin/bash -c "true < /dev/tcp/localhost/25 && true < /dev/tcp/localhost/143 && true < /dev/tcp/localhost/1025 && true < /dev/tcp/localhost/1143" CMD /bin/bash -c \
"true < /dev/tcp/localhost/25 \
&& true < /dev/tcp/localhost/143 \
&& true < /dev/tcp/localhost/1025 \
&& true < /dev/tcp/localhost/1143"
ENTRYPOINT ["/protonmail/entrypoint.sh"] ENTRYPOINT ["/protonmail/entrypoint.sh"]
CMD ["run"] CMD ["run"]

View file

@ -31,13 +31,17 @@ RUN apt-get update \
dtach) apt-get install -y --no-install-recommends dtach ;; \ dtach) apt-get install -y --no-install-recommends dtach ;; \
abduco) apt-get install -y --no-install-recommends abduco ;; \ abduco) apt-get install -y --no-install-recommends abduco ;; \
reptyr) apt-get install -y --no-install-recommends reptyr ;; \ reptyr) apt-get install -y --no-install-recommends reptyr ;; \
*) echo "Unsupported PTY_TOOL: ${PTY_TOOL}. Supported values are: dtach, abduco, reptyr." >&2; exit 1 ;; \ *) echo "Unsupported PTY_TOOL: ${PTY_TOOL}. Supported: dtach, abduco, reptyr." >&2 ; exit 1 ;; \
esac \ esac \
&& chmod +x /protonmail/entrypoint.sh \ && chmod +x /protonmail/entrypoint.sh \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=30s --timeout=5s --retries=3 --start-period=120s \ HEALTHCHECK --interval=30s --timeout=5s --retries=3 --start-period=120s \
CMD /bin/bash -c "true < /dev/tcp/localhost/25 && true < /dev/tcp/localhost/143 && true < /dev/tcp/localhost/1025 && true < /dev/tcp/localhost/1143" CMD /bin/bash -c \
"true < /dev/tcp/localhost/25 \
&& true < /dev/tcp/localhost/143 \
&& true < /dev/tcp/localhost/1025 \
&& true < /dev/tcp/localhost/1143"
ENTRYPOINT ["/protonmail/entrypoint.sh"] ENTRYPOINT ["/protonmail/entrypoint.sh"]
CMD ["run"] CMD ["run"]