cargo-chef

repository·main·Indexed 25 days ago

https://github.com/lukemathwalker/cargo-chef

A cargo sub-command designed to build project dependencies for optimal Docker layer caching. It provides a two-step workflow using 'prepare' to generate a recipe.json and 'cook' to build dependencies, significantly speeding up Rust compilation times in Docker builds. The tool includes pre-built Docker images and a library API for programmatic control over recipes, skeletons, and cooking arguments.

Tokens
3.6K
Snippets
9
Records
21
Agent score
82%

What's inside cargo-chef

  1. Build static binaries for Alpine using cargo-chef

    main

    To run your application in an alpine distribution, you must build a fully static binary (e.g., for x86_64-unknown-linux-musl). When cross-compiling, you must explicitly specify the --target flag in both cargo chef cook and cargo build.

    # Using muslrust as base
    FROM clux/muslrust:stable AS chef
    USER root
    RUN cargo install --locked cargo-chef
    WORKDIR /app
    
    FROM chef AS planner
    COPY . .
    RUN cargo chef prepare --recipe-path recipe.json
    
    FROM chef AS builder
    COPY --from=planner /app/recipe.json recipe.json
    # Specify the target for cooking
    RUN cargo chef cook --release --target x86_64-unknown-linux-musl --recipe-path recipe.json
    COPY . .
    # Specify the target for building
    RUN cargo build --release --target x86_64-unknown-linux-musl --bin app
    
    FROM alpine AS runtime
    COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/app /usr/local/bin/
    CMD ["/usr/local/bin/app"]
  2. Implement cargo-chef in a Dockerfile

    main

    To speed up Docker builds, use a multi-stage build pattern. The planner stage generates the recipe, the builder stage uses that recipe to cache dependencies via cook, and the final stage runs the application.

    Important: You must use the same Rust version in all stages to ensure caching works correctly.

    FROM lukemathwalker/cargo-chef:latest-rust-1 AS chef
    WORKDIR /app
    
    FROM chef AS planner
    COPY . .
    RUN cargo chef prepare --recipe-path recipe.json
    
    FROM chef AS builder 
    COPY --from=planner /app/recipe.json recipe.json
    # Build dependencies - this is the caching Docker layer!
    RUN cargo chef cook --release --recipe-path recipe.json
    # Build application
    COPY . .
    RUN cargo build --release --bin app
    
    # Runtime stage
    FROM debian:trixie-slim AS runtime
    WORKDIR /app
    COPY --from=builder /app/target/release/app /usr/local/bin
    ENTRYPOINT ["/usr/local/bin/app"]
  3. Use lukemathwalker/cargo-chef pre-built Docker images

    main

    Pre-built images are available on DockerHub, equipped with both Rust and cargo-chef.

    Tagging Scheme: <cargo-chef version>-rust-<rust tag> (e.g., 0.1.74-rust-1.56.0).

    Available Aliases:

    • latest-rust-<alias>: Latest cargo-chef version with a specific Rust version.
    • <cargo-chef version>-rust-<alias>: Specific cargo-chef version with a specific Rust version.
    • latest: Latest cargo-chef version and latest Rust version.
  4. Use cargo chef prepare to create a recipe

    main

    The prepare subcommand analyzes your project to determine the minimum subset of files (specifically Cargo.lock and Cargo.toml manifests) required to build dependencies. It generates a recipe.json file which acts as the input for the cook command.

    cargo chef prepare --recipe-path recipe.json
  5. Use the Recipe API to prepare and cook a recipe

    main
    The Recipe struct is the primary interface for managing the cargo-chef workflow. You can use Recipe::prepare to derive a project skeleton from a base path and then use Recipe::cook to execute the dependency caching process using provided CookArgs.
  6. Skeleton Data Structures

    main

    The Skeleton struct represents the captured state of a Rust workspace. It is serializable/deserializable via serde.

    Skeleton

    • manifests: A list of Manifest objects.
    • config_file: An optional string containing .cargo/config.toml content.
    • lock_file: An optional string containing Cargo.lock content.
    • rust_toolchain_file: An optional tuple containing the RustToolchainFile type and the file content.

    Manifest

    • relative_path: The path to the manifest relative to the project root.
    • contents: The raw string content of the manifest.
    • targets: A list of Target objects defined in the manifest.
  7. Build a minimum project from a skeleton

    main

    Use Skeleton::build_minimum_project to reconstruct a valid Rust project on disk from a Skeleton instance. This is intended to be called on an empty directory.

    It performs the following:

    • Writes Cargo.lock if available.
    • Writes rust-toolchain or rust-toolchain.toml if available.
    • Writes .cargo/config.toml if available.
    • Writes all Cargo.toml manifests.
    • Creates dummy entrypoint files (lib.rs, main.rs, build.rs) for all targets to ensure the project is syntactically valid for compilation.
  8. Remove compiled dummy artifacts

    main

    Use Skeleton::remove_compiled_dummies to clean up compilation artifacts generated by the dummy entrypoint files (like lib.rs or build.rs) created during the skeleton build process. This prevents compilation errors caused by leftover dummy files in the target directory.

    Arguments:

    • base_path: The root of the project.
    • profile: The OptimisationProfile to clean (e.g., Debug or Release).
    • target: (Optional) A list of specific target names to clean.
    • target_dir: (Optional) A custom path to the target directory.
  9. Derive a project skeleton from a base path

    main

    Use Skeleton::derive to scan a directory and generate a Skeleton object containing all necessary Cargo manifests, configuration files, lockfiles, and toolchain information.

    If a member name is provided, the resulting skeleton will be filtered to include only the transitive dependency closure required for that specific workspace member (package or binary target).