cargo-reduce-recipe reduces cargo-chef recipes for multi-member workspaces by removing dependencies that are unrelated to the targeted member. This results in improved Docker caching.
Consider a workspace like this:
├── Cargo.toml
├── bar
└── foobar and foo are completely independent.
However, when using cargo-chef, adding a new dependency to foo will still force bar to be rebuilt even if you run:
cargo chef prepare --recipe-path recipe-bar.json --bin bar The issue is that cargo-chef’s generated recipe still includes all workspace members manifests and lockfiles even those that are unrelated to the filtered member.
As a result a change in foo’s dependencies invalidates the Docker cache for bar.
cargo-reduce-recipe fixes that. It post-processes the generated recipe and removes all dependency and lockfile entries that are not actually required by the selected workspace member (directly or transitively). The result is a minimized recipe ensuring that unrelated workspace changes no longer trigger unnecessary rebuilds.
In a real-life unpublished workspace, using cargo-reduce-recipe cut Docker build times for unrelated members for me from 80s to 20s, a ~75% reduction.
cargo install --git https://github.com/preiter93/cargo-reduce-recipe --tag v0.1.0To build dependency recipes for only a specific workspace member, follow this:
- Prepare a recipe for a single member
cargo chef prepare --recipe-path recipe-bar.json --bin bar - Reduce the recipe
cargo-reduce-recipe \
--recipe-path-in recipe-bar.json \
--recipe-path-out recipe-bar-reduced.json \
--bin bar- Cook the reduced recipe
cargo chef cook --release --recipe-path recipe-bar-reduced.json --bin barLicense: MIT
cargo-reduce-recipe can be used together with cargo-chef in a Dockerfile:
ARG SERVICE_NAME
FROM rust:1.88-bookworm AS chef
WORKDIR /services
# Install cargo-chef and cargo-reduce-recipe
RUN cargo install cargo-chef --locked --version 0.1.73 \
&& cargo install --git https://github.com/preiter93/cargo-reduce-recipe --tag v0.1.0
# Prepare and reduce the recipe
FROM chef AS planner
ARG SERVICE_NAME
ENV SERVICE_NAME=${SERVICE_NAME}
COPY . .
RUN cargo chef prepare --recipe-path recipe.json --bin ${SERVICE_NAME} \
&& cargo-reduce-recipe --recipe-path-in recipe.json --recipe-path-out recipe-reduced.json --bin ${SERVICE_NAME}
# Build the dependencies
FROM chef AS builder
ARG SERVICE_NAME
ENV SERVICE_NAME=${SERVICE_NAME}
COPY --from=planner /services/recipe-reduced.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json --bin ${SERVICE_NAME}
# Build the binary
COPY . .
RUN cargo build --release --bin ${SERVICE_NAME}
# Run the service
FROM debian:bookworm-slim AS runtime
ARG SERVICE_NAME
COPY --from=builder /services/target/release/${SERVICE_NAME} /usr/local/bin/main
ENTRYPOINT ["/usr/local/bin/main"]This functionality should probably be part of cargo-chef itself, see LukeMathWalker/cargo-chef#323.