Publishing a Rust crate to crates.io can seem daunting at first, but with this comprehensive guide, you'll have your crate published in no time. This guide covers everything from setting up your project to announcing your release.
Table of Contents
- Prerequisites
- Project Setup
- Writing Your Code
- Local Development & Testing
- Setting Up GitHub
- Setting Up crates.io
- Publishing Your Crate
- Post-Publication
- Troubleshooting
Prerequisites
Before you start, ensure you have:
- Rust installed - Install from rustup.rs
- Git installed - Download from git-scm.com
- GitHub account - Sign up at github.com
- Basic Rust knowledge - Understanding of cargo and basic Rust syntax
Verify your installations:
rustc --version
cargo --version
git --version
Project Setup
1. Create Your Project
# Create a new library crate
cargo new --lib hello-world-lib
cd hello-world-lib
# Or for a binary crate
cargo new hello-world-bin
cd hello-world-bin
2. Configure Cargo.toml
Your Cargo.toml is crucial for crates.io. Here's a comprehensive example:
[package]
name = "hello-world-lib"
version = "0.1.0"
edition = "2021"
rust-version = "1.70.0" # Minimum Rust version required
authors = ["Your Name <your.email@example.com>"]
license = "MIT OR Apache-2.0"
description = "A simple hello world library for demonstration"
documentation = "https://docs.rs/hello-world-lib"
homepage = "https://github.com/yourusername/hello-world-lib"
repository = "https://github.com/yourusername/hello-world-lib"
readme = "README.md"
keywords = ["hello", "world", "example", "demo", "tutorial"]
categories = ["development-tools", "no-std"]
exclude = [
".github/",
"tests/fixtures/",
"*.log",
]
[dependencies]
# Add your dependencies here
[dev-dependencies]
# Add test/benchmark dependencies here
[features]
default = []
# Configure docs.rs to show all features
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
Important Fields Explained:
- name: Must be unique on crates.io (check availability first)
- version: Follow Semantic Versioning
-
license: Use SPDX identifiers. Common:
MIT,Apache-2.0,MIT OR Apache-2.0 - keywords: Max 5, help with discoverability
- categories: Choose from crates.io categories
- exclude: Files to exclude from the published package
3. Create Essential Files
Create these files in your project root:
README.md:
Create a file named README.md with the following content:
# hello-world-lib
[](https://crates.io/crates/hello-world-lib)
[](https://docs.rs/hello-world-lib)
[](https://github.com/yourusername/hello-world-lib#license)
A simple hello world library for demonstration purposes.
## Installation
Add this to your Cargo.toml:
[dependencies]
hello-world-lib = "0.1"
## Usage
use hello_world_lib::greet;
fn main() {
let message = greet("World");
println!("{}", message);
}
## License
Licensed under either of:
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE))
- MIT license ([LICENSE-MIT](LICENSE-MIT))
at your option.
LICENSE-MIT:
MIT License
Copyright (c) 2026 Your Name
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
LICENSE-APACHE:
Download from: https://www.apache.org/licenses/LICENSE-2.0.txt
CHANGELOG.md:
Create a file named CHANGELOG.md:
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.1.0] - 2026-01-22
### Added
- Initial release
- greet() function for hello world greeting
.gitignore:
/target/
Cargo.lock # Include for binaries, exclude for libraries
**/*.rs.bk
*.pdb
.DS_Store
Writing Your Code
src/lib.rs
Here's a simple example with proper documentation:
//! # hello-world-lib
//!
//! A simple library for greeting people.
//!
//! ## Example
//!
//! Use the greet function like this:
//!
//! use hello_world_lib::greet;
//!
//! let message = greet("World");
//! assert_eq!(message, "Hello, World!");
/// Greets a person by name.
///
/// # Arguments
///
/// * `name` - The name of the person to greet
///
/// # Examples
///
/// Basic usage:
///
/// use hello_world_lib::greet;
///
/// let message = greet("Alice");
/// assert_eq!(message, "Hello, Alice!");
///
pub fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
/// A more advanced greeting with customization.
///
/// # Arguments
///
/// * `name` - The name of the person to greet
/// * `enthusiastic` - If true, adds exclamation marks
///
/// # Examples
///
/// With enthusiasm:
///
/// use hello_world_lib::greet_custom;
///
/// let message = greet_custom("Bob", true);
/// assert_eq!(message, "Hello, Bob!!!");
///
pub fn greet_custom(name: &str, enthusiastic: bool) -> String {
if enthusiastic {
format!("Hello, {}!!!", name)
} else {
format!("Hello, {}.", name)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_greet() {
assert_eq!(greet("World"), "Hello, World!");
assert_eq!(greet("Rust"), "Hello, Rust!");
}
#[test]
fn test_greet_custom() {
assert_eq!(greet_custom("Alice", false), "Hello, Alice.");
assert_eq!(greet_custom("Bob", true), "Hello, Bob!!!");
}
#[test]
fn test_empty_name() {
assert_eq!(greet(""), "Hello, !");
}
}
Note: In the actual Rust code file, you should use proper doc test syntax with triple backticks. The indentation shown above is only for display purposes in this markdown guide. When writing your actual src/lib.rs file, use the standard Rust doc comment format with /// and triple backticks for code examples.
Key Points for Good Documentation:
-
Module-level docs (
//!) - Describe what your crate does -
Function docs (
///) - Document every public item - Examples in docs - These run as tests!
- Use proper markdown - Headers, lists, code blocks
- Document panics - If a function can panic, document when
Local Development & Testing
Before publishing, thoroughly test your crate locally.
1. Run Tests
# Run all tests
cargo test
# Run tests with all features enabled
cargo test --all-features
# Run tests with output
cargo test -- --nocapture
# Run a specific test
cargo test test_greet
# Run tests and show ignored tests
cargo test -- --ignored
2. Check Code Quality with Clippy
Clippy is Rust's linter that catches common mistakes:
# Install clippy (if not already installed)
rustup component add clippy
# Run clippy
cargo clippy
# Run clippy with all features and treat warnings as errors
cargo clippy --all-features -- -D warnings
# Fix some issues automatically
cargo clippy --fix
3. Format Your Code
# Install rustfmt (if not already installed)
rustup component add rustfmt
# Check formatting
cargo fmt --all -- --check
# Format code
cargo fmt --all
4. Build Documentation
# Build documentation
cargo doc
# Build and open documentation
cargo doc --open
# Build with all features
cargo doc --all-features --no-deps --open
# Check for broken documentation links
cargo doc --no-deps
5. Check Documentation Tests
Documentation examples are tested:
# Test documentation examples
cargo test --doc
6. Run Benchmarks (Optional)
If you have benchmarks:
cargo bench
7. Check for Security Vulnerabilities
# Install cargo-audit
cargo install cargo-audit
# Run security audit
cargo audit
8. Dry Run Publishing
This checks if your crate can be published without actually publishing:
cargo publish --dry-run
This will:
- Package your crate
- Verify all files are included
- Build the crate
- Run tests
- Check for errors
Common issues caught by dry-run:
- Missing license files
- Incorrect paths in Cargo.toml
- Missing dependencies
- Documentation errors
Setting Up GitHub
1. Create a Repository
Go to github.com/new and create a new repository:
-
Repository name:
hello-world-lib - Description: "A simple hello world library for demonstration"
- Public/Private: Choose Public (required for crates.io)
- Initialize with: Leave unchecked (you already have files)
2. Initialize Git Locally
# Initialize git repository
git init
# Add all files
git add .
# Create initial commit
git commit -m "Initial commit"
# Add remote
git remote add origin https://github.com/yourusername/hello-world-lib.git
# Push to GitHub
git push -u origin main
3. Set Up Branch Protection (Optional but Recommended)
Go to: Settings → Branches → Add rule
Recommended settings:
- ✅ Require pull request reviews before merging
- ✅ Require status checks to pass before merging
- ✅ Require branches to be up to date before merging
4. Set Up GitHub Actions for CI/CD (Recommended)
Create .github/workflows/ci.yml:
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
CARGO_TERM_COLOR: always
jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo index
uses: actions/cache@v4
with:
path: ~/.cargo/git
key: ${{ runner.os }}-cargo-git-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo build
uses: actions/cache@v4
with:
path: target
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
- name: Run tests
run: cargo test --all-features --verbose
- name: Run clippy
run: cargo clippy --all-features -- -D warnings
- name: Check formatting
run: cargo fmt --all -- --check
docs:
name: Documentation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Build documentation
run: cargo doc --all-features --no-deps
Commit and push:
git add .github/workflows/ci.yml
git commit -m "Add CI workflow"
git push
Setting Up crates.io
1. Create a crates.io Account
Option A: Sign in with GitHub (Recommended)
- Go to crates.io
- Click "Log in with GitHub"
- Authorize the application
- Important: Verify your email address
Option B: Create a standalone account
- Go to crates.io
- Create an account with email
- Verify your email address
2. Verify Your Email
⚠️ CRITICAL: You must verify your email before publishing!
- Check your email inbox
- Click the verification link
- Confirm email is verified in Account Settings
Without email verification, cargo publish will fail with:
error: api errors (status 403 Forbidden): Please verify your email address
3. Generate an API Token
- Go to Account Settings
- Click "New Token"
- Give it a name (e.g., "My Laptop - Jan 2026")
- Optional but recommended: Set an expiration date
- Copy the token immediately (you won't see it again!)
Token format: cio_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
4. Configure Cargo with Your Token
Option A: Interactive Login
cargo login
# Paste your token when prompted
Option B: Direct Login
cargo login cio_your_token_here
This saves your token to ~/.cargo/credentials.toml:
[registry]
token = "cio_your_token_here"
Security Tips:
- ⚠️ Never commit this file to git
- ⚠️ Never share your token
- ⚠️ Rotate tokens regularly
- ⚠️ Use different tokens for different machines
Publishing Your Crate
Pre-Publication Checklist
Before publishing, verify everything one last time:
# 1. Ensure you're on the main branch
git checkout main
git pull origin main
# 2. Verify all changes are committed
git status
# 3. Run all tests
cargo test --all-features
# 4. Run clippy
cargo clippy --all-features -- -D warnings
# 5. Check formatting
cargo fmt --all -- --check
# 6. Build documentation
cargo doc --all-features --no-deps
# 7. Dry-run publish
cargo publish --dry-run
Create and Push a Git Tag
Tags mark specific versions in your git history:
# Create an annotated tag
git tag -a v0.1.0 -m "Release version 0.1.0"
# Verify the tag was created
git tag -l
# View tag details
git show v0.1.0
# Push the tag to GitHub
git push origin v0.1.0
# Or push all tags
git push origin --tags
Tag Naming Convention:
- Use
vprefix:v0.1.0,v1.0.0 - Follow semantic versioning:
vMAJOR.MINOR.PATCH - Create annotated tags (with
-a), not lightweight tags
Publish to crates.io
# Publish your crate
cargo publish
What happens:
- Cargo packages your crate (creates a
.cratefile) - Uploads to crates.io
- crates.io verifies and extracts the package
- Your crate becomes available at
https://crates.io/crates/your-crate-name - docs.rs automatically builds documentation
Expected output:
Packaging hello-world-lib v0.1.0
Verifying hello-world-lib v0.1.0
Compiling hello-world-lib v0.1.0
Finished dev [unoptimized + debuginfo] target(s) in 1.23s
Uploading hello-world-lib v0.1.0
Monitor docs.rs Build
After publishing, docs.rs will build your documentation:
- Go to
https://docs.rs/crate/your-crate-name - Wait for the build to complete (usually 5-15 minutes)
- Check for any build errors
If docs.rs build fails:
- Check the build log at
https://docs.rs/crate/your-crate-name/0.1.0/builds - Fix issues and publish a patch version (0.1.1)
Post-Publication
1. Create a GitHub Release
Go to: https://github.com/yourusername/hello-world-lib/releases/new
Fill in:
-
Tag: Select
v0.1.0(the tag you created) -
Release title:
v0.1.0 - Initial Release - Description: Use content like this:
# hello-world-lib v0.1.0 - Initial Release 🎉
First official release!
## Features
- Simple greet() function
- Custom greetings with greet_custom()
- Comprehensive documentation
- Full test coverage
## Installation
Add to your Cargo.toml:
[dependencies]
hello-world-lib = "0.1"
## Documentation
- [API Documentation](https://docs.rs/hello-world-lib)
- [crates.io](https://crates.io/crates/hello-world-lib)
## Changelog
See CHANGELOG.md for details.
Click Publish release.
2. Verify Everything Works
Test that users can actually use your crate:
# Create a test project
cd /tmp
cargo new test-my-crate
cd test-my-crate
# Add your crate
cargo add hello-world-lib
# Test it
cat >> src/main.rs << 'EOF'
use hello_world_lib::greet;
fn main() {
println!("{}", greet("World"));
}
EOF
cargo run
3. Update README Badges
Update your README with working badges:
[](https://crates.io/crates/hello-world-lib)
[](https://docs.rs/hello-world-lib)
[](https://github.com/yourusername/hello-world-lib#license)
[](https://github.com/yourusername/hello-world-lib/actions)
[](https://crates.io/crates/hello-world-lib)
4. Announce Your Release 📢
Share your crate with the community:
Reddit - r/rust:
Title: [Release] hello-world-lib 0.1.0 - Simple greeting library
Body:
I just published my first crate! It's a simple library for greeting people.
Features:
- Easy to use API
- Well documented
- Full test coverage
Would love feedback from the community!
Crates.io: https://crates.io/crates/hello-world-lib
GitHub: https://github.com/yourusername/hello-world-lib
Twitter/X:
🚀 Just published my first Rust crate!
hello-world-lib v0.1.0 - A simple greeting library
📦 cargo add hello-world-lib
📚 https://docs.rs/hello-world-lib
🔗 https://crates.io/crates/hello-world-lib
#rustlang #rust
This Week in Rust:
Submit to: https://this-week-in-rust.org/
Rust Users Forum:
Post in the "Announcements" category: https://users.rust-lang.org/c/announcements
Discord/Slack:
Share in relevant Rust communities.
5. Monitor and Respond
After publishing:
- ✅ Check crates.io for download stats
- ✅ Monitor GitHub for issues and PRs
- ✅ Respond to questions and feedback
- ✅ Fix bugs quickly with patch releases
Troubleshooting
Common Issues and Solutions
Issue: "api errors (status 403 Forbidden): Please verify your email"
Solution: Verify your email address on crates.io
# 1. Go to https://crates.io/settings/profile
# 2. Verify your email
# 3. Try publishing again
Issue: "crate name is already reserved"
Solution: Choose a different name
# Check if a name is available
cargo search your-crate-name
# Update Cargo.toml with new name
# Update all documentation with new name
Issue: "failed to verify the checksum"
Solution: Clean and retry
rm -rf target/package
cargo clean
cargo publish --dry-run
cargo publish
Issue: "the remote server responded with an error"
Solution: Check your credentials
# Re-login
cargo login
# Verify your token is saved
cat ~/.cargo/credentials.toml
Issue: "package has no license specified"
Solution: Add license field to Cargo.toml
[package]
license = "MIT OR Apache-2.0"
Issue: "package has no description"
Solution: Add description to Cargo.toml
[package]
description = "A helpful description of your crate"
Issue: docs.rs build failed
Solution: Check the build log
- Go to
https://docs.rs/crate/your-crate-name/0.1.0/builds - Read the error messages
- Fix locally and test with
cargo doc --all-features - Publish a patch version
Issue: "cannot publish with feature X without dependency Y"
Solution: Add missing dependency or remove feature
[features]
my-feature = ["dep:some-crate"]
[dependencies]
some-crate = { version = "1.0", optional = true }
Issue: "version 0.1.0 already exists"
Solution: You cannot republish the same version
# Increment version in Cargo.toml
version = "0.1.1"
# Update CHANGELOG.md
# Commit changes
# Create new tag
git tag -a v0.1.1 -m "Patch release"
git push origin v0.1.1
# Publish new version
cargo publish
Getting Help
If you're stuck:
- Read the error message carefully - Cargo gives helpful errors
- Check the Cargo Book - https://doc.rust-lang.org/cargo/
- Search GitHub Issues - https://github.com/rust-lang/cargo/issues
- Ask on Reddit - r/rust
- Ask on Discord - Rust Community Discord
- Ask on Users Forum - https://users.rust-lang.org/
Maintaining Your Crate
Releasing New Versions
Patch Release (0.1.1) - Bug fixes only:
# 1. Update version in Cargo.toml
version = "0.1.1"
# 2. Update CHANGELOG.md
# 3. Commit changes
git add .
git commit -m "Bump version to 0.1.1"
# 4. Create tag
git tag -a v0.1.1 -m "Patch release 0.1.1"
# 5. Push
git push origin main
git push origin v0.1.1
# 6. Publish
cargo publish
Minor Release (0.2.0) - New features, backward compatible:
# Same process, but update to 0.2.0
version = "0.2.0"
Major Release (1.0.0) - Breaking changes:
# Same process, but update to 1.0.0
version = "1.0.0"
# Important: Document breaking changes clearly
Yanking a Version
If you published a version with critical issues:
# Yank version (prevents new projects from using it)
cargo yank --vers 0.1.0
# Undo yank
cargo yank --vers 0.1.0 --undo
Note: Yanking doesn't delete the version, it just discourages its use.
Best Practices
Code Quality
- ✅ Write comprehensive tests
- ✅ Add documentation examples (they're tested!)
- ✅ Use
cargo clippyregularly - ✅ Format code with
cargo fmt - ✅ Keep dependencies minimal
- ✅ Use feature flags for optional functionality
Documentation
- ✅ Write clear README with examples
- ✅ Document all public APIs
- ✅ Include examples in documentation
- ✅ Keep CHANGELOG.md updated
- ✅ Add inline comments for complex code
Versioning
- ✅ Follow Semantic Versioning strictly
- ✅ Never break backward compatibility in patch/minor versions
- ✅ Document all changes in CHANGELOG.md
- ✅ Tag all releases in git
Security
- ✅ Never commit credentials
- ✅ Run
cargo auditregularly - ✅ Keep dependencies updated
- ✅ Use GitHub Dependabot
- ✅ Rotate API tokens regularly
Community
- ✅ Respond to issues promptly
- ✅ Review PRs thoroughly
- ✅ Be welcoming to contributors
- ✅ Write CONTRIBUTING.md guide
- ✅ Add CODE_OF_CONDUCT.md
Conclusion
Congratulations! You now know how to publish a Rust crate from start to finish. Remember:
- Test thoroughly - Users depend on your code
- Document well - Future you will thank you
- Version carefully - Breaking changes need major version bumps
- Engage with community - Rust community is friendly and helpful
- Keep learning - There's always more to discover
Happy publishing! 🚀
Quick Reference Commands
# Development
cargo test --all-features
cargo clippy --all-features -- -D warnings
cargo fmt --all
cargo doc --all-features --no-deps --open
cargo publish --dry-run
# Git & GitHub
git tag -a v0.1.0 -m "Release v0.1.0"
git push origin v0.1.0
# Publishing
cargo login
cargo publish
# Maintenance
cargo yank --vers 0.1.0
cargo yank --vers 0.1.0 --undo
Additional Resources
- The Cargo Book
- crates.io Policies
- Rust API Guidelines
- Semantic Versioning
- Keep a Changelog
- Choose a License
Written by a Rust enthusiast. Last updated: January 2026
Top comments (0)