From 3a3900e959999815b6b9a61041c601c231e5a3bd Mon Sep 17 00:00:00 2001 From: rays Date: Mon, 9 Nov 2026 14:05:12 +0000 Subject: [PATCH] Image builds keep compiled crates between builds (#438) The ipx Dockerfile built its dependencies in a layer of stub crates, which any change to Cargo.toml or Cargo.lock invalidated: all of them compiled again in release, 405 crates, 170 s at the builder's cap. The directory's had no such layer and compiled everything every time. Both now build with cargo's registry and target/ as BuildKit cache mounts. Measured on Tower: a Cargo.toml change rebuilds three crates in 52 s instead of 405 in 170 s. The peak is about the same, since the ipx crate itself is most of it; what goes is the minutes at the cap. Co-Authored-By: Claude Opus 5.5 --- CLAUDE.md | 4 +++- Dockerfile | 29 +++++++++++++---------------- Dockerfile.directory | 12 ++++++++---- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 967f32b..ba8f0f3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -114,7 +114,9 @@ split lands means the frontend and directory too. Run `pull` before `up`, becaus not Docker Hub, which rate-limits this host with `429 Too Many Requests`. The capped builder cannot see images the Docker daemon has stored, so pulling and tagging them locally does nothing for it. If `buildkitd.toml` changes, remove the builder (`docker buildx rm ipx-capped`) so the -next build makes it again with the new config. +next build makes it again with the new config. The builder also keeps the compiled crates +between builds (cache mounts, #438), so a `Cargo.lock` change compiles only what changed; removing +it throws them away, and the next build of each image compiles all of them again. The containers have no Docker healthcheck (#218): Docker runs one with `exec`, which on Tower fails whenever root is logged out, because elogind removes `/run/user/0`, where containerd puts diff --git a/Dockerfile b/Dockerfile index 931f285..7c5d5dd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,25 +7,22 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && rm -rf /var/lib/apt/lists/* WORKDIR /src -# Dependencies first, so editing the source does not rebuild librqbit every time: each crate of -# the workspace as an empty stub, its manifest real. COPY Cargo.toml Cargo.lock ./ -COPY crates/ipx/Cargo.toml crates/ipx/ -COPY crates/feed/Cargo.toml crates/feed/ -COPY crates/directory/Cargo.toml crates/directory/ -COPY crates/tui/Cargo.toml crates/tui/ -RUN mkdir -p crates/ipx/src crates/feed/src crates/directory/src crates/tui/src \ - && echo 'fn main(){}' > crates/ipx/src/main.rs && touch crates/feed/src/lib.rs \ - && echo 'fn main(){}' > crates/directory/src/main.rs && touch crates/directory/src/lib.rs \ - && echo 'fn main(){}' > crates/tui/src/main.rs \ - && cargo build --release --locked -p ipx \ - && rm -rf crates/ipx/src crates/feed/src crates/directory/src crates/tui/src - COPY crates ./crates # ipx builds the directory crate in, whose public pages serve these (#215). COPY web/logo.svg web/logo-dark.svg web/favicon.png ./web/ -# cargo skips a rebuild if mtimes look untouched; make sure it does not. -RUN touch crates/ipx/src/main.rs crates/ipx/src/lib.rs crates/feed/src/lib.rs && cargo build --release --locked -p ipx +# cargo's registry and target/ are BuildKit cache mounts, kept in the builder between builds, so +# a Cargo.lock change compiles only the crates that changed (#438): a dependency layer of stub +# crates went with any change to it and compiled all 500 again, holding the builder's memory cap +# for minutes. A cache mount is not part of the image, so the binary is copied out in the same +# step. The workspace's own crates are touched because cargo goes by mtimes and skips a rebuild +# if they look untouched. +RUN --mount=type=cache,id=ipx-cargo-registry,target=/usr/local/cargo/registry,sharing=locked \ + --mount=type=cache,id=ipx-target,target=/src/target,sharing=locked \ + touch crates/ipx/src/main.rs crates/ipx/src/lib.rs crates/feed/src/lib.rs \ + crates/directory/src/lib.rs \ + && cargo build --release --locked -p ipx \ + && cp target/release/ipxd /usr/local/bin/ipxd FROM debian:trixie-slim # ffmpeg is DeadAir's (#171): it finds the silences and copies the frames kept. Debian's adds @@ -33,7 +30,7 @@ FROM debian:trixie-slim RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates gosu ffmpeg \ && rm -rf /var/lib/apt/lists/* -COPY --from=build /src/target/release/ipxd /usr/local/bin/ipxd +COPY --from=build /usr/local/bin/ipxd /usr/local/bin/ipxd COPY docker-entrypoint.sh /usr/local/bin/ RUN chmod +x /usr/local/bin/docker-entrypoint.sh diff --git a/Dockerfile.directory b/Dockerfile.directory index 3dbe3f8..ee7adb1 100644 --- a/Dockerfile.directory +++ b/Dockerfile.directory @@ -6,18 +6,22 @@ RUN apt-get update && apt-get install -y --no-install-recommends build-essential && rm -rf /var/lib/apt/lists/* WORKDIR /src # Every member of the workspace, since cargo reads them all to build one. -# ponytail: no dependency layer, so each build compiles sea-orm again; add the stub trick from -# the main Dockerfile if builds get slow enough to matter. COPY Cargo.toml Cargo.lock ./ COPY crates ./crates # The logo and icon the public pages serve (#215), compiled into the binary. COPY web/logo.svg web/logo-dark.svg web/favicon.png ./web/ -RUN cargo build --release --locked -p ipx-directory +# Compiled crates kept in the builder between builds, as in the main Dockerfile (#438). A target +# of its own: built with ipx's, the two would rebuild shared crates for each other's features. +RUN --mount=type=cache,id=ipx-cargo-registry,target=/usr/local/cargo/registry,sharing=locked \ + --mount=type=cache,id=ipx-directory-target,target=/src/target,sharing=locked \ + touch crates/directory/src/main.rs crates/directory/src/lib.rs crates/feed/src/lib.rs \ + && cargo build --release --locked -p ipx-directory \ + && cp target/release/ipx-directory /usr/local/bin/ipx-directory FROM debian:trixie-slim RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \ && rm -rf /var/lib/apt/lists/* -COPY --from=build /src/target/release/ipx-directory /usr/local/bin/ipx-directory +COPY --from=build /usr/local/bin/ipx-directory /usr/local/bin/ipx-directory ENV DIRECTORY_CONFIG=/config/directory.toml # Unraid's nobody:users. It writes nothing to disk: the database is elsewhere. USER 99:100