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.1
# 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 crates/common
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.
Workspace Optimization
workspace-cache analyzes your workspace dependency graph and includes only the necessary members for your binary. This enables isolated builds where changes to unrelated workspace members don't invalidate your cache. For example, if you have user, order, and payment services, changing the payment service won't trigger rebuilds of user or order containers.
This optimization applies to both the dependency build stage and the binary build stage. The generated Dockerfile only copies workspace members that your binary depends on, ensuring that changes to independent members don't invalidate Docker's layer cache at any stage of the build.
Comparison with cargo-chef
| Feature | cargo-chef | workspace-cache |
|---|---|---|
| Cached dependencies | ✅ | ✅ |
| Workspace optimization (dependencies) | ✅ | ✅ |
| Fast dependency build | ❌ | ✅ (--fast) |
| Workspace optimization (binary) | ❌ | ✅ (Optimization) |
| Dockerfile generation | ❌ | ✅ (Dockerfile) |
| GitHub Actions | ❌ | ✅ (CI Usage) |
| Get dependent workspace members | ❌ | ✅ (members) |
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
uses: preiter93/workspace-cache/.github/actions/build-workspace@main
with:
binary: user
working-directory: services
- name: Run tests
working-directory: services/.workspace-cache
run: cargo test -p user --verbose
- name: Clippy
working-directory: services/.workspace-cache
run: cargo clippy -p user -- -D warnings
Important: Run tests and other cargo commands from services/.workspace-cache where the complete workspace is built.
Note: The build action compiles tests by default (build-tests: true), so dev-dependencies are cached and test compilation is fast.
See the action READMEs for more options:
Complete Example with Matrix Strategy
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
service:
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Install workspace-cache
uses: preiter93/workspace-cache/.github/actions/install-workspace-cache@main
- name: Build
uses: preiter93/workspace-cache/.github/actions/build-workspace@main
with:
binary: ${{ matrix.service }}
working-directory: services
- name: Run tests
working-directory: services/.workspace-cache
run: cargo test -p ${{ matrix.service }} --verbose
- name: Clippy
working-directory: services/.workspace-cache
run: cargo clippy -p ${{ matrix.service }} -- -D warnings
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@v5
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
cp -r $path .workspace-cache/$path
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.
CLI Reference
deps - Generate minimal workspace
Generates a minimal workspace with stub sources for the specified binary.
members - List workspace members
Lists workspace members that the binary depends on (used internally by CI actions).
dockerfile - Generate Dockerfile
See Usage section above for details.
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.
Example
See rust-microservices for a complete example using workspace-cache in a real microservices project with Docker and CI/CD.
License
MIT