Below is an example Dockerfile
that we recommend at Depot for building images for Rust applications.
# syntax=docker/dockerfile:1
FROM rust:1.90 AS build
RUN cargo install cargo-chef sccache --locked
ENV RUSTC_WRAPPER=sccache \
SCCACHE_DIR=/sccache
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
RUN cargo chef prepare --recipe-path recipe.json
RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked \
--mount=type=cache,target=/usr/local/cargo/git,sharing=locked \
--mount=type=cache,target=$SCCACHE_DIR,sharing=locked \
cargo chef cook --release --recipe-path recipe.json
COPY . .
RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked \
--mount=type=cache,target=/usr/local/cargo/git,sharing=locked \
--mount=type=cache,target=$SCCACHE_DIR,sharing=locked \
cargo build --release --bin app
FROM ubuntu:24.04 AS runtime
RUN groupadd -g 1001 appgroup && \
useradd -u 1001 -g appgroup -m -d /home/appuser -s /bin/bash appuser
COPY --from=build --chown=appuser:appgroup /app/target/release/app /usr/local/bin/app
USER appuser
ENTRYPOINT ["/usr/local/bin/app"]
At a high level, here are the things we're optimizing in our Docker build for a Rust application:
FROM rust:1.90 AS build
FROM rust:1.90 AS build
RUN cargo install cargo-chef sccache --locked
We use the official Rust 1.90 image as the base for reliable builds. We install cargo-chef for dependency management and sccache for compilation artifact caching.
ENV RUSTC_WRAPPER=sccache \
SCCACHE_DIR=/sccache
We configure sccache by setting RUSTC_WRAPPER=sccache
to wrap Rust compiler calls and SCCACHE_DIR=/sccache
to specify the cache directory location.
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
RUN cargo chef prepare --recipe-path recipe.json
cargo-chef creates a recipe from the dependency files, enabling Docker to cache dependency builds separately from source code changes.
RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked \
--mount=type=cache,target=/usr/local/cargo/git,sharing=locked \
--mount=type=cache,target=$SCCACHE_DIR,sharing=locked \
cargo chef cook --release --recipe-path recipe.json
Dependencies are compiled using cargo-chef with three types of cache mounts:
COPY . .
RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked \
--mount=type=cache,target=/usr/local/cargo/git,sharing=locked \
--mount=type=cache,target=$SCCACHE_DIR,sharing=locked \
cargo build --release --bin app
The application is compiled using the same cache mounts as dependency compilation. This ensures that sccache can reuse compilation artifacts between dependency and application builds.
FROM ubuntu:24.04 AS runtime
FROM ubuntu:24.04 AS runtime
RUN groupadd -g 1001 appgroup && \
useradd -u 1001 -g appgroup -m -d /home/appuser -s /bin/bash appuser
COPY --from=build --chown=appuser:appgroup /app/target/release/app /usr/local/bin/app
USER appuser
ENTRYPOINT ["/usr/local/bin/app"]
The runtime stage uses Ubuntu 24.04 for a reliable runtime environment. We create a non-root user for security and copy the compiled binary from the build stage.
Cache mounts are one of the most powerful features for optimizing Docker builds with Depot. This Dockerfile uses the following cache mount syntax:
RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked \
--mount=type=cache,target=/usr/local/cargo/git,sharing=locked \
--mount=type=cache,target=$SCCACHE_DIR,sharing=locked \
cargo chef cook --release --recipe-path recipe.json
type=cache
: Specifies this is a cache mount that persists across builds.
Multiple cache targets:
/usr/local/cargo/registry
: Cargo package registry cache/usr/local/cargo/git
: Git-based dependency cache$SCCACHE_DIR
: sccache compilation artifact cache (resolves to /sccache
)sharing=locked
: Ensures exclusive access during compilation, preventing cache corruption.
cargo-chef solves a fundamental caching problem in Rust Docker builds. When you run cargo build
, Docker treats the entire compilation as a single operation. Any change to your source code invalidates the cache and forces recompilation of all dependencies.
cargo-chef separates dependency compilation from source compilation by:
cargo chef prepare
: Analyzes Cargo.toml
and Cargo.lock
to create a dependency recipecargo chef cook
: Compiles only the dependencies based on the recipecargo build
: Compiles the application code using cached dependenciesThis separation allows Docker to cache dependency compilation independently, only rebuilding dependencies when they actually change.
Even with cargo-chef separating dependencies from source code, compiling dependencies is still treated as a single operation. If a single dependency changes, all dependencies need to be recompiled.
sccache provides fine-grained caching at the compiler level by:
RUSTC_WRAPPER=sccache
environment variable intercepts compiler invocationsThis means only the specific crates that have changed need to be recompiled, while unchanged crates can reuse their cached artifacts.
For more information regarding Rust cache mounts, please visit the official sccache documentation and cargo-chef documentation.