crate2nix: building a Rust workspace with Nix crate by crate
crate2nix builds your cargo-based rust project crate-by-crate with nix
At a glance
- What is it?
- crate2nix reads Cargo.toml and Cargo.lock, resolves the dependency tree through cargo metadata, and renders a Cargo.nix file containing one Nix derivation per crate, so Nix rebuilds only the parts that changed. It suits a Rust team that wants Nix caching and incremental CI without giving up cargo locally, and it trades the simplicity of cargo build for a generated file you have to keep in step.
- Who is it for?
- crate2nix fits a Rust team moving to Nix for reproducible builds and binary cache sharing, since it produces per-crate derivations so a dependency bump rebuilds only what changed, and since it keeps cargo and rust-analyzer as the local development workflow.
- 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 9 days ago.
- What is it written in?
- Mainly Nix, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem it solves, stated in three lines
crate2nix generates Nix build files for Rust and Cargo projects, building each crate individually for precise, incremental rebuilds. The description is one sentence and it contains the whole design.
The problem is that a Cargo project is a single opaque artifact from the outside. You ask cargo to build a binary, and cargo figures out the whole dependency graph, and the result is a build that Nix cannot cache at a useful granularity. Change a leaf dependency and the whole binary rebuilds, because Nix sees one derivation whose inputs include the entire source tree and the whole toolchain.
crate2nix inverts that. The generated Cargo.nix file contains one Nix derivation per crate, so Nix rebuilds only what changed. The three bullets in the readme spell out the benefits: incremental CI builds where only the crates that actually changed are rebuilt, full Nix integration with remote builds, binary caches, Docker images and NixOS modules, and local dev unchanged, meaning you keep using cargo and rust-analyzer as usual.
That third point is the one that decides whether a team can adopt this at all. A Nix-based Rust workflow that requires developers to use Nix commands for everyday work creates friction that most teams cannot sustain. Keeping cargo for the inner loop and using Nix for the release build is the arrangement that makes this practical, and it is why the tool describes itself in terms of what it does not change.
The project lives under the nix-community organisation, the homepage is nix-community.github.io/crate2nix, the licence is Apache-2.0 with an MIT alternative, the primary language is Nix, and the last push to the repository was on 2026-10-14. The newest release is 0.15.0 from 2026-01-28, before which came 0.14.1 in June 2024 and 0.14.0 in April 2024.
Running it, three ways
The quick start has three routes, and they differ in how much you commit to installing anything.
The first needs no installation at all:
nix run nixpkgs#crate2nix -- generate
nix build -f Cargo.nix rootCrate.buildRunning from nixpkgs means you always get whatever version nixpkgs pins rather than the newest release, which is a reasonable trade for a command you run occasionally.
The second is the flake template, which scaffolds a project already set up for this:
nix flake init --template github:nix-community/crate2nixThe third is installing it, either from nixpkgs as a profile package or from the GitHub repository for the latest development version:
# From nixpkgs
nix profile install nixpkgs#crate2nix
# Latest development version
nix profile install github:nix-community/crate2nixOnce it is installed, the two commands that matter inside your project are:
crate2nix generate # creates Cargo.nix
nix build -f Cargo.nix rootCrate.buildThat is the whole workflow: generate, then build. The generated file is what Nix consumes, and the name rootCrate.build is the entry point for the top-level crate of the project.
The repository layout shows how much machinery sits behind those two commands. There is a crate2nix/ directory holding the generator itself, written in Rust since the tool shells out to cargo, a templates/ directory with the Tera templates that render the output, a lib/ directory with the Nix-side library including the JSON consumer, a nix/ directory, a tools.nix exposing the helpers, a default.nix, and a flake.nix with a flake.lock. There are sample_projects/ and sample_workspace/ directories for working examples, a nix-test.sh and run_tests.sh and tests.nix for the test harnesses, shell.nix and an .envrc for the development environment, and formatting and regeneration scripts including regenerate_cargo_nix.sh.
How the generation works and the two strategies
The how it works section is short enough to quote almost exactly, and it explains the whole design.
crate2nix reads Cargo.toml and Cargo.lock, resolves the full dependency tree via cargo metadata, prefetches source hashes, and renders a Cargo.nix file through Tera templates. The generated file contains one Nix derivation per crate, so Nix rebuilds only what changed.
Each step matters. Reading both files means the lock file's exact versions are respected rather than re-resolved. cargo metadata is what gives the tool the real graph, including features and platform conditionals, rather than a guess made from parsing TOML. Prefetching source hashes is what lets the derivations point at fixed inputs, which is what makes the build cacheable at all. And rendering through templates means the output is a normal Nix file you can read and patch.
Then there are the two strategies, presented as a table with pros and cons:
Manual, which is crate2nix generate, has no IFD and full build parallelism, but you must regenerate when dependencies change.
Auto, which is Import From Derivation, is always in sync with Cargo.lock, but may reduce parallelism.
That trade is the one to think about. The manual strategy gives a Cargo.nix that is a static file, so every crate in the workspace can be built in parallel, and the cost is that the file goes stale the moment you touch Cargo.toml. The auto strategy derives the file at build time from Cargo.lock, so it can never be stale, and the cost is that the derivation chain acquires a dependency and the parallelism of the build is reduced.
The Import From Derivation approach also has a practical wrinkle that is not spelled out here: it means the Nix build depends on having Cargo and the Rust toolchain available at evaluation time, and it makes the build graph less transparent, since the dependency list is computed rather than written down.
The Nix API and the experimental JSON path
tools.nix exposes helpers for use in your own Nix expressions, so a project can consume the generated file rather than running the generator by hand.
let
crate2nix = builtins.fetchTarball "https://github.com/nix-community/crate2nix/tarball/master";
tools = import "${crate2nix}/tools.nix" { inherit pkgs; };
generated = tools.generatedCargoNix {
name = "my-project";
src = ./.;
};
project = pkgs.callPackage "${generated}/default.nix" {};
in
project.rootCrate.buildThe shape is a fetchTarball of the crate2nix repository, an import of tools.nix, a call to generatedCargoNix with a name and a source, and then a callPackage of the generated default.nix. There is also a shorthand, appliedCargoNix, which combines generation and import.
The JSON output path is marked experimental and is the more interesting development. crate2nix generate --format json emits a pre-resolved JSON file instead of Cargo.nix, and the reason given is that all dependency resolution, meaning feature expansion, cfg() platform filtering and optional dependency activation, happens in Rust, so the Nix side is a trivial data consumer with no O(n times m) eval-time logic.
That is a real architectural argument. Evaluating a large dependency graph in the Nix evaluator is slow and hard to debug, and moving it into a Rust program that already has the information makes the Nix side a lookup rather than a computation. The output is written to ./Cargo.json by default, with -o to override, and the readme notes it is compact because empty fields and already-resolved feature maps are omitted, so the JSON is typically smaller than the equivalent Cargo.nix.
The consumer is lib/build-from-json.nix, which the readme says is shipped in the repository:
let
cargoNix = import ./lib/build-from-json.nix {
inherit pkgs;
src = ./.;
resolvedJson = ./Cargo.json;
};
in {
# Single crate
my-binary = cargoNix.workspaceMembers.my-crate.build;
# Root crate (if the workspace has one)
default = cargoNix.rootCrate.build;
# All workspace members linked together
all = cargoNix.allWorkspaceMembers;
}It accepts two optional arguments for customisation, buildRustCrateForPkgs to override the buildRustCrate used, for example for a custom toolchain, and defaultCrateOverrides for per-crate build fixups, the same as the existing Cargo.nix workflow.
Where crate2nix is the wrong choice
The first limitation is that it is a lot of machinery for a small problem. If your Rust project is one crate with a handful of dependencies, the standard cargo2nix or the plain Nix buildRustPackage from nixpkgs does the job with far less machinery. The per-crate granularity pays off when you have a workspace of many crates and a dependency graph where most of them do not change between commits.
The second is that you now have two descriptions of the dependency graph. Cargo.toml and Cargo.lock describe it for Cargo, and the generated Cargo.nix describes it for Nix, and they can disagree. The manual strategy's weakness is exactly this, and the readme is upfront that you must regenerate when dependencies change. A team that forgets produces a build that is stale, and the failure is confusing because the build succeeds against the old dependency set.
The third is that the generated file is generated. When a build fails inside Cargo.nix, you are debugging a file you did not write, and the debugging path goes through the generator and the templates. There is per-crate override support, but overrides are another layer of state to maintain, and they are another thing that can drift.
The fourth is the toolchain coupling. The generator is a Rust program, so getting crate2nix itself is one problem, and the generated derivations assume a particular buildRustCrate and a particular Rust toolchain. The JSON consumer's buildRustCrateForPkgs argument exists precisely to override the buildRustCrate, for example for a custom toolchain, and that is the escape hatch for when the defaults do not match.
The fifth is the release cadence and the split between Rust and Nix. The newest release is 0.15.0 from January 2026, with the previous two in 2024, so the version series has been moving in large jumps. And the project now spans two languages, with the generator in Rust and the Nix side in Nix, which means a bug in the dependency resolution and a bug in the Nix evaluation are found and fixed by different parts of the project, and a contributor has to be comfortable in both to reason about the whole pipeline.
Editorial conclusion
crate2nix fits a Rust team moving to Nix for reproducible builds and binary cache sharing, since it produces per-crate derivations so a dependency bump rebuilds only what changed, and since it keeps cargo and rust-analyzer as the local development workflow. It is the wrong choice if your project is a single crate with few dependencies, because the generated file is then more machinery than the problem needs, and it is the wrong choice if you cannot keep a generated Cargo.nix in step with Cargo.lock. Before adopting it, decide between the manual and the auto strategy, since the manual route requires regenerating whenever dependencies change, and note that the last push was on 2026-10-14 with version 0.15.0 published on 2026-01-28 under an Apache-2.0 licence with an MIT alternative.
Frequently asked questions
What does crate2nix do?
It generates Nix build files for Rust and Cargo projects, building each crate individually so Nix rebuilds only what changed. It reads Cargo.toml and Cargo.lock, resolves the dependency tree via cargo metadata, prefetches source hashes, and renders a Cargo.nix file through Tera templates with one derivation per crate.
How do I use crate2nix without installing it?
Run nix run nixpkgs#crate2nix -- generate and then nix build -f Cargo.nix rootCrate.build. You can also scaffold a project with nix flake init --template github:nix-community/crate2nix, or install it with nix profile install nixpkgs#crate2nix or from the GitHub repository for the latest development version.
What is the difference between the manual and auto crate2nix generation strategies?
Manual, which is crate2nix generate, has no IFD and full build parallelism, but the file must be regenerated whenever dependencies change. Auto, which is Import From Derivation, stays in sync with Cargo.lock automatically, but may reduce build parallelism.
What does the experimental JSON output of crate2nix do?
crate2nix generate --format json emits a pre-resolved Cargo.json instead of Cargo.nix, with all dependency resolution, feature expansion, cfg() platform filtering and optional dependency activation done in Rust so the Nix side is a trivial data consumer with no eval-time logic. You consume it with lib/build-from-json.nix.
What licence is crate2nix released under?
Apache-2.0, with a LICENSE-APACHE file at the root of the repository alongside a LICENSE-MIT file, so either licence can be used. The last push was on 2026-10-14 and the newest release is 0.15.0 from 2026-01-28.
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/nix-community-crate2nix)Was this useful?