Back to Blog

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:

Bash
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:

TOML
[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"
FieldRequiredNotes
nameYesMust be unique on crates.io
versionYesSemVer format
editionRecommended2021 or 2024
descriptionYesPublish fails without it
licenseYesSPDX identifier
repositoryRecommendedGitHub/GitLab URL
readmeOptionalDefaults to README.md
keywordsOptionalUp to 5, helps discoverability
categoriesOptionalFrom crates.io/categories

3. License

The Rust ecosystem standard is dual MIT + Apache 2.0. Create two files in your project root:

  • LICENSE-MIT
  • LICENSE-APACHE

In Cargo.toml:

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:

Markdown
# 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:

Bash
cargo publish --dry-run

This will:

  1. Package your crate (shows which files are included)
  2. Build from the package to verify it compiles
  3. Not actually upload anything

Common Errors

TEXT
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

Bash
cargo publish

That's it. On success:

TEXT
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.

TEXT
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:

TOML
# 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 version from crates.io

Publish in Order

Bash
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:

TEXT
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:

  1. Bump version in Cargo.toml
  2. Update the version in dependent crates too
  3. cargo publish
TOML
# 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 yank them (blocks new installs, existing users unaffected).
  • Crate names are first-come, first-served.
  • Sensitive data (API keys, .env) cannot be removed once published.
Bash
# 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:

TOML
[package]
exclude = ["tests/fixtures/*", "benches/*", ".github/*"]

Or whitelist only what you need:

TOML
[package]
include = ["src/**/*", "Cargo.toml", "LICENSE-*", "README.md"]

Pre-Publish Checklist

  • Cargo.toml has name, version, description, license, repository
  • LICENSE file(s) exist in project root
  • README.md exists
  • cargo publish --dry-run passes
  • 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.

Comments