How to Publish a Rust Library to crates.io
You wrote a Rust library. Now you want others to use it with cargo add. Here's everything you need to know to publish to crates.io — from setup to multi-crate workspaces.
1. Create a crates.io Account
Sign in at crates.io with your GitHub account. Then go to Account Settings → API Tokens and generate a token.
Log in from your terminal:
cargo login <YOUR_API_TOKEN>
This only needs to be done once. The token is saved to ~/.cargo/credentials.toml.
2. Required Cargo.toml Fields
crates.io will reject your publish if these are missing:
[package]
name = "my-awesome-lib"
version = "0.1.0"
edition = "2024"
description = "A brief description (required!)"
license = "MIT OR Apache-2.0"
repository = "https://github.com/username/my-awesome-lib"
| Field | Required | Notes |
|---|---|---|
name | Yes | Must be unique on crates.io |
version | Yes | SemVer format |
edition | Recommended | 2021 or 2024 |
description | Yes | Publish fails without it |
license | Yes | SPDX identifier |
repository | Recommended | GitHub/GitLab URL |
readme | Optional | Defaults to README.md |
keywords | Optional | Up to 5, helps discoverability |
categories | Optional | From crates.io/categories |
3. License
The Rust ecosystem standard is dual MIT + Apache 2.0. Create two files in your project root:
LICENSE-MITLICENSE-APACHE
In Cargo.toml:
license = "MIT OR Apache-2.0"
Why dual? Users can pick whichever suits them. MIT is simple and permissive. Apache 2.0 adds patent protection. Most major Rust crates (tokio, serde, clap) use this combination.
4. README.md
This is displayed on your crates.io page. At minimum include:
# my-awesome-lib
One-line description.
## Installation
\```toml
[dependencies]
my-awesome-lib = "0.1"
\```
## Usage
\```rust
use my_awesome_lib::something;
fn main() {
// example
}
\```
## License
MIT OR Apache-2.0
5. Dry Run
Always test before publishing:
cargo publish --dry-run
This will:
- Package your crate (shows which files are included)
- Build from the package to verify it compiles
- Not actually upload anything
Common Errors
error: `description` is missing
→ Add description to Cargo.toml
error: `license` is missing
→ Add license to Cargo.toml
error: package exceeds maximum size (10MB)
→ Use [package] exclude in Cargo.toml to remove large files
6. Publish
cargo publish
That's it. On success:
Uploading my-awesome-lib v0.1.0
Published my-awesome-lib v0.1.0 at registry `crates-io`
Anyone can now run cargo add my-awesome-lib.
7. Publishing a Multi-Crate Workspace
If your project has multiple crates, you must publish them in dependency order.
my-core → no deps (publish first)
my-render → depends on core (publish second)
my-lib → depends on both (publish third)
my-cli → depends on all (publish last)
Specify Both path and version
Locally, you reference crates by path. But crates.io needs a version. Use both:
# Before (local only — won't publish)
my-core = { path = "../my-core" }
# After (publishable)
my-core = { version = "0.1.0", path = "../my-core" }
How it works:
- Local build: Cargo uses the
path - Published crate: Cargo resolves by
versionfrom crates.io
Publish in Order
cargo publish -p my-core
# Wait 1-2 minutes for crates.io index to update
cargo publish -p my-render
cargo publish -p my-lib
cargo publish -p my-cli
Use the -p flag to specify which crate. Wait between each step — the previous crate must be indexed before the next one can resolve its dependency.
Rate Limits
crates.io throttles rapid publishing. If you publish too many crates in succession:
error: 429 Too Many Requests
Please try again after [timestamp]
Space out your publishes by 1-2 minutes if you have more than ~5 crates.
8. Version Updates
To release a new version:
- Bump
versioninCargo.toml - Update the version in dependent crates too
cargo publish
# Bug fix: 0.1.0 → 0.1.1
# New feature: 0.1.0 → 0.2.0
# Breaking: 0.x.x → 1.0.0
version = "0.1.1"
SemVer rules:
- patch (0.1.0 → 0.1.1): Bug fixes, backwards compatible
- minor (0.1.0 → 0.2.0): New features, backwards compatible
- major (0.x → 1.0): Breaking changes
9. Things You Can't Undo
- Published versions cannot be deleted. You can only
yankthem (blocks new installs, existing users unaffected). - Crate names are first-come, first-served.
- Sensitive data (API keys, .env) cannot be removed once published.
# Yank a version (prevent new installs)
cargo yank --version 0.1.0
# Undo a yank
cargo yank --version 0.1.0 --undo
10. Excluding Files
Prevent unnecessary files from being packaged:
[package]
exclude = ["tests/fixtures/*", "benches/*", ".github/*"]
Or whitelist only what you need:
[package]
include = ["src/**/*", "Cargo.toml", "LICENSE-*", "README.md"]
Pre-Publish Checklist
-
Cargo.tomlhas name, version, description, license, repository - LICENSE file(s) exist in project root
- README.md exists
-
cargo publish --dry-runpasses - No sensitive data included in the package
- For workspaces: dependencies already published to crates.io
- For workspaces: path dependencies have version specified
Check all boxes, and you're ready to ship.