cargo release: the dry run that stands between your crate and crates.io
Cargo subcommand `release`: everything about releasing a rust crate.
At a glance
- What is it?
- A cargo subcommand that checks your repository state, bumps versions across a workspace, runs publish, tags and pushes, and by default does none of it until you pass --execute. Useful for maintainers of multi crate workspaces, with one documented output bug worth knowing about.
- Who is it for?
- Adopt cargo release if you publish crates regularly, especially from a workspace where a version bump has to propagate to dependents, since the dry run default means you can see the whole plan before anything is irreversible. Do not adopt it for a library you consume rather than publish, since its versioning tracks binary compatibility and the README tells library users to pin with an equals requirement.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Dry run is the default, and that is the design
Most release tooling either does the work or does not. cargo release does neither by default, which is the single decision that makes it worth trying.
The invocation is a level and a flag:
$ cargo release [level]
$ cargo release [level] --executeWithout --execute, the command runs in dry run mode and prints what it would do. The README's phrasing is that you can safely run it and verify what it will do, which is the correct expectation for a tool that talks to crates.io, creates git tags and pushes commits.
Two knobs shape the dry run itself. Logging gets more detailed with each additional -v. And --no-verify skips the cargo publish verify step, which the README recommends specifically for making dry runs faster. That flag is a real trade: the verify step is what catches a package that does not build, and skipping it makes the preview faster and less informative at the same time.
The prerequisite is minimal and non negotiable: your project has to be managed by git. Every feature below depends on that.
State validation is what makes the plan trustworthy
Before anything else, the tool checks that you are in a good state for a release. The README lists three conditions: you are on the right branch, you are up to date with the remote, and the tree is clean.
This is the part that most improves on doing it by hand. A release cut from a dirty tree produces a tag that does not match the commit you tested, and a release cut from a branch that is behind the remote produces a crate nobody can rebuild. Both are caught here as preconditions rather than discovered by a consumer.
The clean tree check also implies a habit: a release should be a commit, not a state of your working directory. cargo release pushes, so it needs something to push.
Note what is not checked. There is no verification that the code builds, no test run, and no changelog check as a gate. Those are left to your own tooling because cargo release is about the mechanics of publishing rather than about quality. If your CI runs tests on the branch before you get here, that is the layer doing that work.
Workspace support is where the real complexity sits
cargo release supports workspaces using cargo's native flags, meaning --workspace, --exclude and --package work the way they do in other cargo commands rather than through a separate configuration vocabulary. That matters more than it sounds, because people who script cargo commands already know these flags.
Three workspace specific behaviours are listed. Dependent crates in the workspace are updated when a version changes, so a bump of a shared library propagates to the crates that name it. Change detection helps guide you toward crates that might not need a release, which is the inverse operation and the harder one. And commits can optionally be shared, so one version bump across several crates lands as a single commit rather than a sequence.
Change detection deserves a note. In a workspace where every crate is versioned together, a mechanical approach publishes everything on every release, which fills crates.io with pointless versions. Detecting which crates actually changed is what lets you keep publishing selectively while still bumping all of them.
The one caveat in the README is worth reading before you interpret output. The packaging and verifying steps list the current version, not the bumped one, because cargo release delegates to cargo publish, which does not know about the in-memory version bump. It is tracked as issue 890. So a dry run showing the old version in the package step is expected behaviour, not a sign your bump did not apply.
Pre-release search and replace is the feature teams actually adopt it for
The pre-release step is a configurable search and replace applied before the version bump lands, and the README names two uses: maintaining the changelog and updating tags inside Dockerfiles.
The README presents both as problems that can be addressed before a release. A changelog has a version heading that must be written. A Dockerfile that pins a git tag has a string that must move with the release. Both are the kind of edit that gets forgotten until a consumer reports it, and both are exactly what a scripted replace is good at.
The configuration for this, and for everything else, is a release.toml at the repository root. The repository also has committed.toml at the root, which is a separate file for settings that should be committed, plus hook.sh, which is the pre-release hook the README refers to for extra customisation. The README's feature list names the hook as the place for CHANGELOG generation.
So there are three layers: built in defaults, a committed configuration file, and a shell hook you can put anything in. The hook is the escape hatch that makes this usable in a repository whose release process is genuinely unusual, and it is also the layer where mistakes become your own.
Documentation is split into a reference at docs/reference.md for the level argument, the other CLI arguments and the configuration file format, and a FAQ at docs/faq.md for adapting the tool to your workflow.
Versioning tracks the binary, not the API
This is the paragraph most library users will skim and should not.
The README states that cargo release's versioning tracks compatibility for the binaries, not the API, and that the crate is uploaded to crates.io to distribute the binary. Library users are told to pin the version with an = version requirement operator.
That is unusual for a cargo subcommand, most of which follow the usual semantic versioning expectations and which library users depend on. Here, a patch release can break your build if you depend on the crate as a library, and the fix is an exact pin. The reason given is distribution: because the release process is what is distributed, compatibility is judged on whether an installed binary still works.
For an adopter the practical consequence is narrow. If you only ever run cargo release as a command line tool, this paragraph does not affect you, though you may want to track the binary version yourself. If you put it in your own build as a library, an equals requirement means you will be updating that pin deliberately rather than as part of a routine cargo update.
The repository's own build metadata is worth a glance for a different reason. The workspace declares edition 2024, an MSRV of Rust 1.92, and a wide clippy lint configuration with many checks set to warn and a few explicitly allowed. That tells you the project takes lint output seriously, which matters for a tool whose output is consumed by a human reading a diff.
Where it is the wrong tool, and what to use instead
The alternatives are listed in the README itself, which is more honest than most. The README lists release-pr, cargo-smart-release, cargo-set-version, cargo-unleash, release-plz, and cargo-workspaces as related tools.
Choose differently depending on one thing: who pushes. cargo release commits and pushes on your machine, which suits a maintainer working locally and a small team with a straightforward tag convention. If your process requires a pull request, a review, or a signed tag enforced by policy, an action based tool is a better shape, and the README's release-pr link points at exactly that.
There are two hard limits. It requires git, so a crate published from an exported tarball or a vendored source tree cannot use it at all. And it delegates the actual publishing to cargo publish.
One more practical warning. Because the tool pushes, a mistake in your release.toml or your hook.sh is not a dry run away from being wrong. Read the dry run output as a diff to be reviewed, not as a progress bar.
Editorial conclusion
Adopt cargo release if you publish crates regularly, especially from a workspace where a version bump has to propagate to dependents, since the dry run default means you can see the whole plan before anything is irreversible. Do not adopt it for a library you consume rather than publish, since its versioning tracks binary compatibility and the README tells library users to pin with an equals requirement. Before your first real release, run it with no arguments and read the dry run output line by line, add repeated -v flags to see the version bump decisions, and confirm your committed.toml and release.toml settings match your actual tagging convention before using --execute.
Frequently asked questions
How do I install and run cargo release?
Install it with cargo install cargo-release, then run cargo release [level] to see a dry run and cargo release [level] --execute to perform the release. Your project has to be managed by git.
Is cargo release safe to run by default?
Yes. It runs in dry run mode unless you pass --execute, printing what it would do rather than publishing, tagging and pushing. Add repeated -v flags for more detail, and --no-verify to skip the publish verify step for a faster preview.
Why does the dry run output show the old version number?
Because the packaging and verifying steps are delegated to cargo publish, which does not know about the in memory only version bump. The README notes this is expected and points at issue 890.
How do I configure cargo release for a workspace?
Workspaces use cargo's native flags such as --workspace, --exclude and --package. Dependent crates are updated when a version changes, change detection suggests which crates may not need a release, and commits can optionally be shared. Settings live in release.toml and committed.toml, with hook.sh as the pre-release hook.
Can I depend on cargo release as a library?
Possible but with a caveat. The README says cargo-release's versioning tracks compatibility for the binaries rather than the API, and advises pinning the version with an = requirement operator if you use it as a library.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/crate-ci-cargo-release)Was this useful?