Cargo Workspaces: Shared Deps & Multi-Crate Commands

Max WellsMax WellsFounder of Rustify

TL;DR: A Cargo workspace is a directory containing multiple crates ([[workspace.members]]) that share a single Cargo.lock and build cache. Use workspaces to split a large project into focused crates; a core library, a CLI binary, a web server; while keeping them versioned and compiled together. Run cargo build --workspace to build all, or cargo build -p crate-name for one. Define shared dependency versions in [workspace.dependencies] to avoid version drift across crates.


What Is a Cargo Workspace?

A workspace is a set of Rust crates in a single repository that share one Cargo.lock, one build output directory (target/), and coordinated dependency versions.

my-project/
├── Cargo.toml          ← workspace root (no [package])
├── Cargo.lock          ← shared lock file
├── target/             ← shared build output
├── core/               ← library crate
│   ├── Cargo.toml
│   └── src/lib.rs
├── server/             ← binary crate
│   ├── Cargo.toml
│   └── src/main.rs
└── cli/                ← binary crate
    ├── Cargo.toml
    └── src/main.rs

Root Cargo.toml:

[workspace]
members = ["core", "server", "cli"]
resolver = "2"  # always use resolver 2 for feature unification

How Do You Create a Workspace?

Create a root Cargo.toml with [workspace], then cargo new each member crate.

mkdir my-project && cd my-project
 
# Create workspace root Cargo.toml
cat > Cargo.toml << 'EOF'
[workspace]
members = ["core", "server", "cli"]
resolver = "2"
EOF
 
# Create member crates
cargo new core --lib
cargo new server
cargo new cli
 
# Build everything
cargo build --workspace

How Do You Share Dependencies Across Crates?

Use [workspace.dependencies] to define versions once; member crates opt in with { workspace = true }.

Root Cargo.toml:

[workspace]
members = ["core", "server", "cli"]
resolver = "2"
 
[workspace.dependencies]
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
sqlx = { version = "0.8", features = ["postgres", "runtime-tokio"] }
thiserror = "2"
anyhow = "1"
tracing = "0.1"

Member server/Cargo.toml:

[package]
name = "server"
version = "0.1.0"
edition = "2021"
 
[dependencies]
tokio = { workspace = true }         # uses version from workspace
serde = { workspace = true }
sqlx = { workspace = true }
core = { path = "../core" }          # local crate dependency

All crates use the same dependency version; no drift, no conflicting lock entries.


How Do You Run Commands on Specific Crates?

# Build all crates
cargo build --workspace
 
# Build one crate
cargo build -p server
 
# Test all
cargo test --workspace
 
# Test one crate
cargo test -p core
 
# Run a binary from a specific crate
cargo run -p cli -- --help
 
# Check all without building
cargo check --workspace
 
# Clippy on all
cargo clippy --workspace

What Are the Benefits of a Workspace?

BenefitDetail
Shared Cargo.lockAll crates use identical dependency versions
Shared build cacheCompile a crate once, reuse across members
Centralized dependency versions[workspace.dependencies] prevents version drift
Clear boundariesEach crate has its own API, tests, and docs
Selective compilationBuild/test only the crate you're working on
Publish independentlyRelease crates separately to crates.io

Frequently Asked Questions

A workspace is Cargo's implementation of a monorepo for Rust crates. The workspace root is the monorepo root; all Rust crates live under it and share tooling. A monorepo can contain non-Rust code outside the workspace.

Yes; each crate specifies its own edition in its Cargo.toml. But all crates share the same resolver set in the workspace root.

Prefer a "virtual workspace" (root Cargo.toml has [workspace] but no [package]). This keeps the root clean. Only make the root a crate if you have a strong reason (rare).

cargo new new-crate --lib
# Then add "new-crate" to [workspace] members in root Cargo.toml

No; Cargo does not support nested workspaces. All crates must be listed in the top-level workspace members.


Sources


  • Cargo: The build system workspaces extend
  • Module: Modules organize code within a crate; workspaces organize crates
  • Feature Flags: Feature unification works differently in workspaces
  • Clap: CLI apps often live inside dedicated workspace crates alongside shared libraries

Keep Reading

Ready to Land a $120k+ Rust Job in the US or Europe?