Optimal Dockerfile for Rust with cargo-chef and sccache
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"]Explanation of the Dockerfile
At a high level, here are the things we're optimizing in our Docker build for a Rust application:
- Multi-stage builds with standard Rust base and Ubuntu runtime
- cargo-chef for dependency separation and caching
- sccache for individual compilation artifact caching
- BuildKit cache mounts for persistent caching
- Security optimizations with non-root users
Stage 1: FROM rust:1.90 AS build
FROM rust:1.90 AS build
RUN cargo install cargo-chef sccache --lockedWe 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.
sccache configuration
ENV RUSTC_WRAPPER=sccache \
SCCACHE_DIR=/sccacheWe configure sccache by setting RUSTC_WRAPPER=sccache to wrap Rust compiler calls and SCCACHE_DIR=/sccache to specify the cache directory location.
Dependency preparation with cargo-chef
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
RUN cargo chef prepare --recipe-path recipe.jsoncargo-chef creates a recipe from the dependency files, enabling Docker to cache dependency builds separately from source code changes.
Dependency compilation with cache mounts
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.jsonDependencies are compiled using cargo-chef with three types of cache mounts:
- Registry cache: Downloaded crate files from crates.io
- Git cache: Git-based dependencies
- sccache: Individual compilation artifacts
Application compilation
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 appThe application is compiled using the same cache mounts as dependency compilation. This ensures that sccache can reuse compilation artifacts between dependency and application builds.
Stage 2: 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.
Understanding BuildKit Cache Mounts
Cache mounts in this Dockerfile speed up builds by persisting the package manager cache. This means that even when a layer needs to be rebuilt, your package manager only fetches what's new or updated. 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.jsonCache Mount Parameters Explained
-
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.
Using cargo-chef for dependency management
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: AnalyzesCargo.tomlandCargo.lockto create a dependency recipecargo chef cook: Compiles only the dependencies based on the recipecargo build: Compiles the application code using cached dependencies
This separation allows Docker to cache dependency compilation independently, only rebuilding dependencies when they actually change.
Using sccache for additional optimization
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:
- Wrapping rustc calls: The
RUSTC_WRAPPER=sccacheenvironment variable intercepts compiler invocations - Caching compilation artifacts: Individual object files and compilation outputs are cached
- Reusing artifacts: Unchanged code can reuse cached compilation results
- Cross-context sharing: Artifacts can be shared between dependency and application builds
This 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.