Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 21 additions & 34 deletions tunnel-node/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# syntax=docker/dockerfile:1
#
# Multi-stage build for the mhrv-tunnel-node service.
#
# Build stage compiles a release binary against rust 1.85 (matches MSRV in
# Cargo.toml). Cargo's incremental build cache is mounted via BuildKit
# `--mount=type=cache` so a `docker build` against an unchanged dependency
# tree skips re-downloading + re-compiling crates — first build ~6 min,
# warm builds ~30 s.
# Build stage compiles a release binary on a recent stable Rust.
# Dependency caching is done via `cargo-chef`: a separate layer cooks
# just the dependencies first, so warm rebuilds where only `src/`
# changes reuse that layer and skip recompiling crates.
#
# This intentionally avoids BuildKit `--mount=type=cache` directives so
# the Dockerfile builds on classic Docker daemons too — notably Cloud
# Run's `gcloud run deploy --source .` builder, which does not enable
# BuildKit (see issue #620).
#
# Runtime stage is `debian:bookworm-slim` for libc compatibility (the
# binary dynamically links against glibc) plus `ca-certificates` so HTTPS
Expand All @@ -27,43 +29,28 @@
# `--health-cmd 'curl -fsS http://localhost:8080/ || exit 1'` on the
# `docker run` if you want compose-level health gating.

FROM rust:1.85-slim AS builder
FROM rust:1.90-slim AS chef
RUN cargo install cargo-chef --locked --version 0.1.77
WORKDIR /app
# Copy lockfile so cargo uses pinned versions identically to local builds.

FROM chef AS planner
COPY Cargo.toml Cargo.lock ./
COPY src/ ./src/
# BuildKit cache mounts: cargo's registry/git caches and the target/
# directory persist across builds, dramatically speeding up rebuilds when
# only application code changes.
#
# `id=...-$TARGETPLATFORM` is load-bearing on multi-arch builds. Without
# it, BuildKit defaults to a single shared cache across architectures
# and the `linux/amd64` + `linux/arm64` jobs race on the same on-disk
# `/usr/local/cargo/registry/src/.../<crate>/.cargo-ok` extraction. The
# second-arriving arch hits `File exists (os error 17)` mid-unpack and
# the whole multi-arch build fails. Per-platform cache id keeps each
# arch's cache isolated; warm-build speedup is preserved per-arch.
# `target` cache is also platform-scoped because target/ holds object
# files for one ABI and sharing them across arches would just produce
# misses or, worse, invalid linking.
ARG TARGETPLATFORM
RUN --mount=type=cache,target=/usr/local/cargo/registry,id=cargo-registry-${TARGETPLATFORM} \
--mount=type=cache,target=/usr/local/cargo/git,id=cargo-git-${TARGETPLATFORM} \
--mount=type=cache,target=/app/target,id=app-target-${TARGETPLATFORM} \
cargo build --release --bin tunnel-node && \
RUN cargo chef prepare --recipe-path recipe.json

FROM chef AS builder
COPY --from=planner /app/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json
COPY Cargo.toml Cargo.lock ./
COPY src/ ./src/
RUN cargo build --release --bin tunnel-node && \
cp /app/target/release/tunnel-node /usr/local/bin/tunnel-node

FROM debian:bookworm-slim
# `ca-certificates` for HTTPS upstream targets; nothing else needed at
# runtime since the binary is statically linked against musl-equivalents
# only for the parts that don't touch glibc.
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/*

# Non-root runtime user. The service does no filesystem writes outside
# /tmp, so a static-uid unprivileged user is sufficient and prevents
# accidental host-FS writes if the container is volume-mounted.
RUN useradd --system --uid 1000 --no-create-home --shell /usr/sbin/nologin tunnel

COPY --from=builder /usr/local/bin/tunnel-node /usr/local/bin/tunnel-node
Expand Down
Loading