This is a hack for incremental Docker builds with Rust.
If you follow a naive approach, that is, just copy your files and run
cargo build, Rust will download and compile all your dependencies each time you build your image.
The layer before the compilation step will change each time you modify your code and
cargo build has no option to only build dependencies.
First create a dummy Cargo package with a library target, copy manifest and lock file, and then perform a release build of the library. I use the library target to avoid having to supply dummy files for (potentially multiple) binary targets specified in the manifest. As long as your dependencies don't change, the layer created by the first release build can be re-used.
After this first step, copy all your files and perform another release build.
It's important to update the timestamp of your
touching the file), otherwise its last modification will predate the last build, so it won't be compiled and you'll run into errors.
RUN cargo init --lib --vcs none COPY Cargo.toml Cargo.lock ./ RUN cargo build --release --lib COPY . . RUN touch src/lib.rs && cargo build --release
Here is a complete Dockerfile for a Rust project building a binary target
bin-target for reference.
The final image doesn't need the Rust toolchain because we simply copy the binary.
FROM rust:1.66-slim AS builder WORKDIR /app RUN cargo init --lib --vcs none COPY Cargo.toml Cargo.lock ./ RUN cargo build --release --lib COPY . . RUN touch src/lib.rs && cargo build --release FROM debian:bullseye-slim AS runtime WORKDIR /app COPY --from=builder /app/target/release/bin-target bin-target ENTRYPOINT ["./bin-target"]
Not a hacker? If you don't mind pulling in another dependency, you can use cargo-chef instead, a tool by Luca Palmieri created exactly for this purpose.