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.import
to 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