workspace-cache
Like cargo-chef but focused on Rust workspaces with multiple binaries (microservices). Generates optimized Dockerfiles with proper layer caching.
Installation
Or install from git for the latest development version:
Quick Start
Generate a Dockerfile for your service:
This produces an optimized multi-stage Dockerfile:
# Stage 1: Install workspace-cache tool
FROM rust:1.94-bookworm AS base
WORKDIR /app
RUN cargo install workspace-cache@0.1.0
# Stage 2: Generate minimal workspace with stub sources
FROM base AS planner
COPY . .
RUN workspace-cache deps --bin user
# Stage 3: Build dependencies only (cached until Cargo.toml/Cargo.lock change)
FROM base AS deps
COPY --from=planner /app/.workspace-cache .
RUN cargo build --release
# Stage 4: Build the actual binary with real source code
FROM deps AS builder
RUN rm -rf crates/user/src crates/common/src
COPY crates/user crates/user
COPY crates/common crates/common
RUN cargo clean --release -p user -p common
RUN cargo build --release --bin user
# Stage 5: Minimal runtime image
FROM debian:bookworm-slim AS runtime
COPY --from=builder /app/target/release/user /usr/local/bin/user
ENTRYPOINT ["/usr/local/bin/user"]
Build & Run
How It Works
- planner - Creates a minimal workspace with stub sources for dependency resolution
- deps - Builds dependencies (cached until Cargo.toml/Cargo.lock changes)
- builder - Copies real source and builds binary
- runtime - Minimal image with just the binary
When source files change but dependencies don't, Docker skips the deps stage entirely.
Usage
The main command is dockerfile. It generates an optimized Dockerfile for your binary:
Options:
--bin <binary>- Binary to build (required)--profile <profile>- Build profile:releaseordebug(default:release)-o, --output <path>- Output path (default: stdout)--base-image <image>- Base image for build stages (default:rust:1.94-bookworm)--runtime-image <image>- Runtime image (default:debian:bookworm-slim)--tool-version <version>- Version of workspace-cache to install (default: current version)--from-git- Install workspace-cache from git instead of crates.io--fast- Fast mode: skip dependency resolution for faster generation
Examples:
# Generate release Dockerfile (default)
# Generate debug Dockerfile
# Use a specific version
# Install from git (latest dev version)
# Custom base image
Fast Mode
Use --fast to skip dependency resolution. This results in a less optimized cache, but speeds up Docker builds as long as no dependencies have changed.
CI Usage (without Docker)
GitHub Action (Recommended)
The simplest way to use workspace-cache in CI is with the provided composite actions:
- name: Install workspace-cache
uses: preiter93/workspace-cache/.github/actions/install-workspace-cache@main
- name: Build my binary
uses: preiter93/workspace-cache/.github/actions/build-workspace@main
with:
binary: user
This handles all the caching and build steps automatically. The install-workspace-cache action caches the tool binary for faster subsequent runs. See the action READMEs for more options:
Manual Setup
You can also set up workspace-cache manually for more control:
- name: Install workspace-cache
run: cargo install workspace-cache
- name: Generate minimal workspace
run: workspace-cache deps --bin user
- name: Get cache key for dependencies
id: cache-key
run: |
HASH="${{ hashFiles('.workspace-cache/Cargo.lock') }}"
echo "key=${{ runner.os }}-workspace-cache-deps-${HASH}" >> $GITHUB_OUTPUT
- name: Cache dependencies
uses: actions/cache@v4
with:
path: .workspace-cache/target
key: ${{ steps.cache-key.outputs.key }}
restore-keys: |
${{ runner.os }}-workspace-cache-deps-
- name: Build dependencies (cached when Cargo.lock unchanged)
run: cargo build --release
working-directory: .workspace-cache
- name: Copy real sources
run: |
workspace-cache members --bin user | while read path name; do
rm -rf .workspace-cache/$path/src
cp -r $path/src .workspace-cache/$path/src
done
- name: Build binary
run: |
PACKAGES=$(workspace-cache members --bin user | awk '{print "-p " $2}' | tr '\n' ' ')
cargo clean --release $PACKAGES
cargo build --release --bin user
working-directory: .workspace-cache
The cache key is based on the generated .workspace-cache/Cargo.lock, so dependencies are only rebuilt when they change. On cache hits, the dependency build step completes in seconds.
Other Commands
The following commands are mainly for debugging or understanding how the tool works internally.
Generate Minimal Workspace
Options:
--bin <binary>- Only include dependencies for specific binary/binaries-o, --output <dir>- Output directory (default:.workspace-cache)--fast- Fast mode: skip dependency resolution
Examples:
# Generate for all workspace binaries
# Generate for a specific binary
# Generate for multiple binaries
# Custom output directory
Show Workspace Members
Shows which workspace members a binary depends on, with their paths and names:
$ workspace-cache members --bin user
crates/pkg-a pkg_a
crates/pkg-b pkg_b
crates/user user
This output can be used in scripts to dynamically copy sources or generate package lists.
Build Workspace
Options:
--bin <binary>- Only build specific binary/binaries--release- Build in release mode
Examples:
# Build all binaries
# Build specific binary in release mode
Testing
Run unit tests:
Test locally in your workspace:
# Generate minimal workspace for a binary
# Build dependencies
# Copy real sources and build (deps are cached)
Note: This mirrors how the generated Dockerfile works. The key is building
the final binary from within .workspace-cache/ after copying real sources.
License
MIT