Closing the Nix Gap: From Environments to Packaged Applications for Rust
Should I use crate2nix, cargo2nix, or naersk for packaging my Rust application?
— (@jvmncs) January 21, 2025
This tweet shows a common problem in Nix: "Should I use crate2nix, cargo2nix, or naersk for packaging my Rust application?"
devenv solved this for development environments differently: instead of making developers package everything with Nix, we provide tools through a simple languages.rust.enable. You get cargo, rustc, and rust-analyzer in your shell without understanding Nix packaging.
But when you're ready to deploy, you face the same problem: which lang2nix tool should you use? Developers don't want to compare crate2nix vs cargo2nix vs naersk vs crane—they want a tested solution that works.
devenv now provides languages.rust.import, which packages Rust applications using crate2nix. We evaluated the available tools and chose crate2nix, so you don't have to.
We've done this before. In PR #1500, we replaced fenix with rust-overlay for Rust toolchains because rust-overlay was better maintained. Users didn't need to change anything—devenv handled the transition while keeping the same languages.rust.enable = true interface.
One Interface for All Languages
The typical workflow:
- Development: Enable the language (
languages.rust.enable = true) to get tools likecargo,rustc, andrust-analyzer. - Packaging: When ready to deploy, use
languages.rust.importto package with Nix.
The same pattern works for all languages:
{ config, ... }: {
# https://devenv.sh/languages
languages = {
rust.enable = true;
python.enable = true;
go.enable = true;
};
# https://devenv.sh/outputs
outputs = {
rust-app = config.languages.rust.import ./rust-app {};
python-app = config.languages.python.import ./python-app {};
go-app = config.languages.go.import ./go-app {};
};
}
Starting with Rust
languages.rust.import automatically generates Nix expressions from Cargo.toml and Cargo.lock.
Add the crate2nix input:
Import your Rust application:
{ config, ... }:
let
# ./app is the directory containing your Rust project's Cargo.toml
myapp = config.languages.rust.import ./app {};
in
{
# Provide developer environment
languages.rust.enable = true;
# Expose our application inside the environment
packages = [ myapp ];
# https://devenv.sh/outputs
outputs = {
inherit myapp;
};
}
Build your application:
Other Languages
This API extends to other languages, each using the best packaging tool:
We've also started using uv2nix to provide a similar interface for Python in PR #2115.
That's it
For feedback, join our Discord community.
Domen