To create a Cargo workspace, initialize a root project with cargo init --name <name>, add a [workspace] section to its Cargo.toml listing your member paths, and then create or move your individual crates into those paths. This setup allows you to build, test, and publish multiple related crates from a single command while sharing dependencies and versions.
First, create the root directory and initialize it as a workspace. You don't need to add any source code to the root; it acts purely as a configuration hub.
mkdir my-workspace
cd my-workspace
cargo init --name my-workspace
Next, edit the root Cargo.toml to define the workspace members. Remove the default [package] section if it exists, as the root itself isn't a package, and add the [workspace] table pointing to your sub-crates.
# my-workspace/Cargo.toml
[workspace]
members = [
"crate-a",
"crate-b",
]
# Optional: Share dependencies across all members
[workspace.dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.0", features = ["full"] }
Now, create your individual crates inside the workspace directory. You can use cargo new or cargo init for each.
cargo new crate-a
cargo new crate-b
Inside crate-a/Cargo.toml and crate-b/Cargo.toml, you can reference the shared dependencies defined in the root using the workspace = true syntax. This ensures version consistency without repeating version numbers.
# crate-a/Cargo.toml
[package]
name = "crate-a"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = { workspace = true }
crate-b = { path = "../crate-b" } # Reference another workspace member
Once configured, running cargo build or cargo test from the root directory will automatically build and test all members. You can also run cargo check to validate the entire workspace structure. If you need to add a new crate later, simply create the directory and add its path to the members array in the root Cargo.toml.
This structure is ideal for monorepos where multiple crates share logic, need to be versioned together, or depend on one another. It keeps your project organized while leveraging Cargo's dependency resolution across the entire tree.