How to Publish Your First Crate on crates.io

Publish your Rust crate to crates.io by logging in with an API token and running cargo publish.

You built something useful. Now share it.

You spent the weekend writing a parser that handles malformed CSV files without panicking. It works perfectly in your current project. Tomorrow you start a new project and realize you need that exact same parser. Copy-pasting the code feels wrong. You want to type cargo add my-csv-parser and have the compiler fetch your code from a central place, just like you fetch serde or tokio.

Publishing a crate to crates.io turns your local code into a dependency anyone can use. The process involves creating an account, authenticating with an API token, validating your metadata, and uploading a packaged tarball. The registry enforces strict rules to keep the ecosystem reliable. You cannot overwrite a version once published. You cannot delete a crate. The system is designed to be immutable and trustworthy.

The registry checks your homework

crates.io is more than a file host. It is a validated registry. When you upload a crate, the server runs a series of checks before accepting the package. It verifies that your Cargo.toml contains required fields like a license and a description. It ensures the crate name is unique. It confirms the version follows semantic versioning rules. It even attempts to compile your crate to catch basic errors.

This validation protects downstream users. If you publish a crate with a missing license, the registry rejects it. If you publish a crate that doesn't compile, the registry rejects it. This strictness means you need to prepare your crate before running the publish command. A bare-bones cargo init project will fail validation. You need metadata, a license, and a clean build.

Setup and authentication

Publishing requires an account on crates.io and an API token. The token proves you have permission to publish crates under your username. The token is a secret string. Treat it like a password. Never commit it to a repository.

Generate the token from your account settings on the website. Copy the token and authenticate your local cargo installation. The cargo login command stores the token in a credential file on your machine.

# Authenticate cargo with your API token.
# Replace the token with the one from your crates.io settings.
cargo login <your-api-token>

Once logged in, cargo remembers the token for future commands. You only need to run cargo login once per machine. If you rotate your token, run the command again to update the stored credential.

The dry run is your safety net

Never run cargo publish directly. Always run cargo publish --dry-run first. The dry run performs every validation step the registry performs, including packaging the crate and checking metadata. It stops before uploading anything. If the dry run fails, you fix the errors locally. If the dry run succeeds, you are ready to publish.

The dry run catches mistakes that waste time and cause frustration. It detects missing fields, invalid license expressions, and files that shouldn't be included. It simulates the exact tarball that will be uploaded. If the dry run passes, the actual publish will almost certainly succeed.

# Validate everything without uploading.
# This creates a .crate file in target/package/ and checks metadata.
cargo publish --dry-run

Convention aside: The Rust community treats cargo publish --dry-run as mandatory. Every experienced developer runs the dry run before publishing. It is the single most important habit for crate authors. Run it before every single publish.

Metadata that makes your crate discoverable

A publishable crate needs more than source code. It needs metadata in Cargo.toml that tells users what the crate does and how to use it. The registry uses this metadata to populate your crate's page. Missing metadata results in a bare page that looks abandoned.

The description field is required. It appears in search results and on the crate page. Keep it concise and accurate. The license field is required. Use an SPDX expression. The readme field is optional but highly recommended. Point it to README.md and the registry renders the markdown on the crate page.

[package]
name = "my-awesome-parser"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "A robust parser for malformed CSV files with streaming support."
readme = "README.md"
repository = "https://github.com/user/my-awesome-parser"
documentation = "https://docs.rs/my-awesome-parser"
homepage = "https://github.com/user/my-awesome-parser"
keywords = ["csv", "parser", "streaming"]
categories = ["parser-implementations", "data-structures"]

# Specify the minimum Rust version required.
# This helps users with older toolchains avoid build failures.
rust-version = "1.70"

Convention aside: Dual licensing is the standard in Rust. Most crates use MIT OR Apache-2.0. This gives users the flexibility to choose a license that fits their project. You will see this pattern in serde, tokio, hyper, and almost every major crate. Follow the pattern unless you have a specific reason to deviate.

Convention aside: Always set the rust-version field. This field tells cargo the minimum compiler version your crate requires. It prevents users on old toolchains from wasting time trying to build your crate and failing with obscure syntax errors. Cargo uses this field to warn users before attempting a build.

Versioning and the yank mechanism

Crate versions follow semantic versioning. The version string has three parts: major, minor, and patch. Increment the patch version for bug fixes. Increment the minor version for new features that do not break the public API. Increment the major version for breaking changes.

You cannot publish a version that already exists. If you publish 0.1.0, you cannot publish 0.1.0 again. You must bump the version to 0.1.1 or 0.2.0. This immutability ensures that dependencies are stable. If a crate changes unexpectedly, projects that depend on it could break.

If you discover a critical bug in a published version, you can yank it. Yanking removes the version from search results and prevents new users from downloading it. It does not delete the version. Users who already downloaded the crate can still use it. Yanking signals that the version is broken and should not be used.

# Yank a version to prevent new downloads.
# This does not delete the version. Existing downloads remain valid.
cargo yank --version 0.1.0

You can un-yank a version if you fix the issue and want to make it available again. Yanking is a temporary measure. Fix the bug, publish a new version, and consider un-yanking if the original version is safe to use.

Pitfalls and common errors

Publishing fails for predictable reasons. The registry returns clear error messages. Understanding these errors saves time.

Name collision is the most common error. Crate names must be unique. If you try to publish a crate with a name that is already taken, the registry rejects it.

error: failed to publish to registry: name `my-parser` is already taken

Check name availability on the crates.io website before you start writing code. You cannot rename a crate after publishing. If the name is taken, you must choose a different name.

Missing metadata causes validation failures. The registry requires a license and a description. If these fields are missing, the dry run fails.

error: failed to validate package `my-parser`: missing `license` field

Add the required fields to Cargo.toml and run the dry run again.

Including unwanted files bloats the crate. Cargo includes all files in the source directory by default. This can include build artifacts, test data, or private keys. Use the include or exclude fields in Cargo.toml to control what gets packaged.

[package]
# Include only source files and documentation.
# Exclude test data and build artifacts.
include = ["src/**/*", "Cargo.toml", "README.md", "LICENSE"]

Convention aside: Use include rather than exclude. An allowlist is safer than a blocklist. It prevents accidental inclusion of sensitive files. List only the files that belong in the crate.

Workspace publishing requires explicit selection. If your crate is part of a workspace, cargo publish fails unless you specify the package.

error: this workspace contains crates that depend on unpublished crates

Use the -p flag to publish a specific crate.

# Publish a specific crate from a workspace.
cargo publish -p my-crate

When to publish and when to hold back

Publishing makes your code public. Anyone can download it, use it, and depend on it. Consider the implications before publishing.

Use cargo publish when you want to release a stable version to the public registry and accept the responsibility of maintaining a public API. Use cargo publish --dry-run when you want to validate your crate metadata and build artifacts without uploading anything. Use cargo yank when you discover a critical bug in a published version and need to prevent new users from downloading it. Use publish = false in Cargo.toml when a workspace member is internal and should never be uploaded to crates.io. Use a local registry or cargo vendor when you need to share code within an organization without exposing it publicly.

Convention aside: Set publish = false for workspace crates that are not intended for public use. This prevents accidental publishing. If you run cargo publish on the workspace root, cargo skips crates with publish = false. This is a safety mechanism for large workspaces.

Where to go next