CLI tool
foresterre/cargo-msrv avatar
foresterre/cargo-msrv

cargo-msrv: finding the oldest Rust that still builds your crate

🦀 Find the minimum supported Rust version (MSRV) for your project

1,249 stars45 forksRustApache-2.0

At a glance

What is it?
cargo-msrv is a cargo subcommand that works out the minimum supported Rust version for a crate by actually trying to build it with older toolchains, verifies an MSRV a crate author has already declared, and lists the MSRVs your dependencies claim. It comes as a Rustup aware command with JSON output for CI, and it is currently shipping 0.20.0 release candidates.
Who is it for?
cargo-msrv fits a library author whose crate has quietly stopped building on the Rust version it advertises, and a team that wants to publish an MSRV it has actually tested rather than one guessed from a dependency's floor. It does not fit a binary application with no MSRV promise, where the question has no answer worth the compile time, and it does not fit a project that cannot install extra toolchains, since the whole method is to try each candidate version.
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 received new commits within the last day.
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

The MSRV is a promise that decays quietly

cargo-msrv assists you in finding the minimum supported Rust version for a crate, and the problem it solves is the one where the answer in your Cargo.toml stops being true.

A crate declares its MSRV, and dependencies declare theirs. The declared version is a promise to your users that they can build on that compiler. The ways it breaks are quiet. A transitive dependency raises its own floor, and your crate stops compiling on the version you advertised. A new Cargo feature in your build script needs a newer compiler. A macro from a newer standard library lands in your code. Nothing warns you, and the users who discover it are the ones still pinned to an old toolchain, which is exactly the audience the MSRV was for.

The tool's answer to that is empirical rather than analytical. Instead of reasoning about which language features your code uses, it uses a compatibility check command to decide whether a Rust version is compatible or not, and it searches for the lowest version where that command succeeds.

The scope of the project is worth stating, because it is narrow and that is a virtue for a build tool. The README points to a cargo-msrv book for the detailed explanations, and the crate publishes a website. The licence is Apache-2.0, with both an Apache and an MIT licence file in the repository, and the code is written in Rust.

find: the search, and the honest linear version

The main command is cargo msrv find, which finds the MSRV for a Cargo project in your current working directory. Two variations matter.

The first is the path. cargo msrv --path <dir> find points the tool at a project in another directory, and there is a --manifest-path option for pointing at a single Cargo manifest file. That is what you want in a script that checks several crates in a repository.

The second is --linear. The default search is a binary search over the available Rust versions, and --linear walks them one at a time from the lowest. The binary search is fast because it halves the range each step, but it only finds a boundary reliably if the property being tested is monotonic, that is, if compatibility is a prefix property where a version that works implies the versions above it work too. With dependencies in the picture that is often true and sometimes not, because a dependency can raise its floor for one version and lower it for another. The linear search does not assume monotonicity, and it is the slow, certain option.

The index of available Rust versions is a crate of its own in the workspace, cargo-msrv-rust-releases, and the versions are ordered with the semver crate. The tool knows the release list, so the search space is the real published toolchain history rather than what happens to be on the machine.

Rustup is required for the find and verify commands, because the tool has to be able to run a specific toolchain on demand, and that is what rustup provides.

The custom check command is the part that matters

The mechanism that decides compatibility is a command you supply, and that is the most important line in the documentation.

cargo msrv find -- <command> uses <command> as the compatibility check which decides whether a Rust version is compatible or not, and the README says this command should be runnable through rustup as rustup run <toolchain> <command>. The examples given are cargo check --tests and cargo check --ignore-rust-version, the latter only available on Rust 1.56 and newer.

That constraint is what makes the whole design coherent. The toolchain has to be selectable per run, which means rustup, which means the check command has to be invocable under a named toolchain. A check that assumes a specific installed default compiler would break the moment the tool tried an older one.

The two example commands are worth comparing. cargo check --tests type checks the test targets as well as the library, which is a stricter condition and therefore a higher MSRV. cargo check --ignore-rust-version tells Cargo to ignore the declared rust-version fields while checking, which is what you need if the goal is to find the real floor rather than to re-confirm the declared one, since otherwise Cargo itself refuses to build on a version the manifest says is too old and the search reports a false result.

The practical advice that falls out of this is to pick the check command that matches what your users actually run. If they run cargo test, a check that only type checks the library is optimistic.

verify, list, show and set are the other four subcommands

The find command is the headline, but the other subcommands are where the tool earns a place in a release process.

cargo msrv verify verifies the MSRV as specified by a crate author. The author may specify it using the package.rust-version key, which requires Rust 1.56 or newer, or the package.metadata.msrv key in the Cargo manifest. This is the command that catches the decay described earlier, because it checks that the version you promise is the version that actually works.

cargo msrv list displays the MSRVs of your dependencies as specified by their authors. That is a different kind of information from what find computes, and it is worth being precise about the difference. The list is what the dependency claims about itself, which may be wrong, and it is cheap to obtain because it needs no compilation at all. The find result is what actually works in your tree, with your feature flags and your lockfile, and it is expensive because it compiles.

Reading the two together is the useful part. If a dependency declares an MSRV of 1.60 and your own find says 1.75, the gap is somewhere in your own code or your other dependencies, and the list tells you where to look.

cargo msrv show shows the currently specified MSRV, read out of your manifest, and cargo msrv set sets the MSRV of the current crate to a given Rust version. Set is the natural companion to a find run, since it writes the discovered value back into the manifest for you, and the manifest handling is done with toml_edit so the rest of the file keeps its formatting.

Output formats are what make it usable in CI

The option list in the README is where the tool reveals its priorities, and the user output options are the first group.

The --output-format option takes three values. The default is human, a progress bar rendered to stderr. Then json, which prints JSON status updates to stdout, and minimal, which is described as minimal output, usually just the result, such as the MSRV or whether verify succeeded or failed.

The split of streams matters. The progress bar goes to stderr and the results go to stdout, which is the arrangement a CI system needs, because a build log wants the progress and the job wants to parse the result. The minimal format is the one to use when a pipeline only needs to know the answer, and the JSON format is the one to use when something wants to track the value over time or render a badge from it.

There is also --no-user-output, which disables the user output entirely, and it is not just a quiet flag, because it is how you turn the tool into a library call inside a larger script.

The debug output options are the usual ones, --no-log, --log-target with the possible values file and stdout, and --log-level from trace to error, with info as the default. The default log target being a file rather than stdout is deliberate, and it keeps the machine readable output clean when you redirect stdout into a file.

The general option is --path, described as the path to the cargo project directory, and --manifest-path for a single manifest file.

Six ways to install it, and one thing to watch

The install section is thorough, and the options cover the usual distribution needs.

From crates.io, with the stable cargo toolchain.

bash
cargo install cargo-msrv --locked

From the git source, either pinned to a tag or tracking the development branch.

bash
cargo install --git https://github.com/foresterre/cargo-msrv.git --tag v0.19.3 cargo-msrv

With cargo-binstall, which downloads a prebuilt binary.

bash
cargo binstall --no-confirm cargo-msrv

On Arch Linux, from the extra repository, the command is pacman -S cargo-msrv.

And in a container, where the build uses cargo-chef for dependency caching and the runtime stage is a slim Alpine image with the binary copied into /usr/local/bin.

bash
docker build -t cargo-msrv .
docker run -t -v "$(pwd)/Cargo.toml":/app/Cargo.toml cargo-msrv

The volume mount in that Docker example is worth noting, because it is the whole trick: the container has no source tree, so you mount the manifest in and the tool works from it.

The thing to watch is the version mismatch. The git install instruction points at the 0.19.3 tag, while the three most recent releases are 0.20.0-beta.1, beta.2 and beta.3, and the Docker entry point runs cargo-msrv msrv, which is the subcommand form rather than the plain msrv form used in the examples.

Shell completions are generated for bash, elvish, fish, powershell and zsh, with the command cargo-msrv completions <SHELL> printing them to standard output, for example into the fish configuration directory.

The crate layout and the release channel it is on

The repository is a cargo workspace of focused crates, and the names say what each one does.

cargo-msrv-cli is the command line interface, cargo-msrv-context is the resolved configuration of a run, cargo-msrv-reporter is the user facing output, cargo-msrv-rust-releases is the index of available Rust releases, and cargo-msrv-rust-tools covers the cargo and rustup tooling, the Cargo manifest parsing and the dependency graph. cargo-msrv-search holds the search methods and the toolchain compatibility check, cargo-msrv-types holds the types shared with the interface, and msrv is the minimum supported Rust version type itself. The external dependencies are the ones you would expect, camino for UTF-8 paths, cargo_metadata for resolving manifest metadata, semver for the version numbers, toml_edit for reading and writing the Cargo.toml, thiserror for error handling, and the tracing family for logs.

The manifest declares rust-version 1.96 for the tool itself, which is a detail worth pausing on. A tool that exists to tell you the minimum Rust version of other crates requires a very recent Rust to build, and that is a tension the project has accepted. It does not affect the crates you scan, but it does affect who can install it, and on an older workstation you may need the binary distribution or the Docker route rather than building from source.

The release channel is the other thing to note. The three most recent tags are all pre-releases, 0.20.0-beta.1, beta.2 and beta.3, with the last one on 2026-10-14, and the repository was last pushed to on 2026-10-15. So the 0.20 line is in beta, the git install instructions reference the 0.19.3 tag, and the CHANGELOG.md in the repository is where the differences between them are written down.

Editorial conclusion

cargo-msrv fits a library author whose crate has quietly stopped building on the Rust version it advertises, and a team that wants to publish an MSRV it has actually tested rather than one guessed from a dependency's floor. It does not fit a binary application with no MSRV promise, where the question has no answer worth the compile time, and it does not fit a project that cannot install extra toolchains, since the whole method is to try each candidate version. Verify three things before you trust a result: that the compatibility check you pass is the one you actually care about, since cargo check and cargo check --tests can disagree, that the toolchain set you search is the one you intend to support, since a found MSRV is only meaningful relative to the versions tested, and that the version of cargo-msrv you run is the release channel you expect, since the 0.20.0 line is currently in release candidates while the git install instructions point at the 0.19.3 tag.

Frequently asked questions

What does cargo-msrv do?

It finds the minimum supported Rust version for a Cargo project by running a compatibility check command under progressively older toolchains, verifies an MSRV that a crate author has already declared, lists the MSRVs your dependencies specify, shows the MSRV currently in your manifest and can set it to a given version.

What do I need installed to use cargo-msrv?

Rustup is required, because the find and verify commands have to run a specific toolchain on demand, and the compatibility check command has to be runnable through rustup as rustup run <toolchain> <command>. The tool itself installs with cargo install cargo-msrv --locked, or through cargo-binstall, pacman on Arch Linux, or the Docker image.

How do I check that the MSRV in my Cargo.toml is still true?

Run cargo msrv verify. The declared value can come from the package.rust-version key, which needs Rust 1.56 or newer, or from the package.metadata.msrv key. A mismatch means your crate no longer builds on the version you promise, usually because a dependency raised its own floor.

How do I see which Rust versions are actually used by my dependencies?

cargo msrv list displays the MSRVs of your dependencies as specified by their authors. That is what each dependency claims, which is cheaper to obtain than a full search and is the fastest way to spot the dependency that raised the floor of your whole tree.

Can I use cargo-msrv in a CI job?

Yes. The --output-format option takes human, which renders a progress bar to stderr, json, which prints JSON status updates to stdout, and minimal, which usually prints just the result such as the MSRV or whether verify succeeded. The --no-user-output option disables the user output entirely, and the log target defaults to a file so stdout stays clean for parsing.

Official sources

  1. foresterre/cargo-msrv on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/foresterre-cargo-msrv.svg)](https://hysenlabs.com/projects/foresterre-cargo-msrv)