Rust Packages with Cargo
Rust Packages with Cargo
The sandbox cannot talk to crates.io. The boring default is: rustPlatform.buildRustPackage with cargoHash (or cargoLock.lockFile) and a committed Cargo.lock — Crane is optional when that FOD is too coarse.
This is the Rust peer of buildGoModule. cargoSha256 is deprecated; use cargoHash (SRI).
Mental model
Cargo.lock → cargo vendor FOD (cargoHash) → cargo build --offline --release → $out/bin
| Style | When |
|---|---|
cargoHash = "sha256-…" |
26.05 default. One hash of the vendored crates (useFetchCargoVendor is on). |
cargoLock.lockFile = ./Cargo.lock |
Git deps / patches in lockfile; outputHashes per git crate. |
| Crane | Incremental crate FODs (like gomod2nix). Extra flake input. |
| fenix / rust-overlay | Pin rustc newer than nixpkgs. Only when 26.05’s rustc is too old. |
Commit Cargo.lock even for bins (cargo generate-lockfile). Without it you are not reproducible. Filter target/ out of src. cargoSha256 is gone — cargoHash is SRI.
A Cargo workspace is cargoRoot / buildAndTestSubdir, not a second src. strictDeps = true when you have C libs (openssl): pkg-config stays native.
Worked examples
Case 1: buildRustPackage + cargoHash
Save as Cargo.toml (next to src/main.rs and Cargo.lock):
# Cargo.toml
[package]
name = "desk-rust-agent"
version = "0.1.0"
edition = "2021"Save as rust_app.nix:
# rust_app.nix
{ lib, rustPlatform }:
rustPlatform.buildRustPackage {
pname = "desk-rust-agent";
version = "0.1.0";
src = lib.cleanSource ./.;
cargoHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
# 26.05 default is true; set only if a pin still uses the old vendor FOD.
# useFetchCargoVendor = true;
meta = {
description = "Desk Rust agent";
license = lib.licenses.mit;
mainProgram = "desk-rust-agent";
};
}# default.nix
{ pkgs ? import <nixpkgs> { } }:
pkgs.callPackage ./rust_app.nix { }nix-build default.nix
./result/bin/desk-rust-agentProbe cargoHash with lib.fakeHash once; paste got:. Never leave fakeHash on main. edition = "2021" or "2024" as the crate requires.
Case 2: cargoLock.lockFile
# lockfile.nix fragment
{
src = ./.;
cargoLock = {
lockFile = ./Cargo.lock;
outputHashes = {
# only if the lockfile has git sources:
# "some-git-crate-0.1.0" = "sha256-…";
};
};
}Prefer this when the lockfile has git sources. Hash mismatches on git deps are FODs too — probe them. Do not set both cargoHash and cargoLock — pick one.
Workspace (one crate in a repo of several):
# workspace.nix fragment
{
src = lib.cleanSource ./.;
cargoRoot = ".";
buildAndTestSubdir = "crates/desk-agent";
cargoHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
}buildAndTestSubdir is the package Cargo builds. The lockfile at cargoRoot still vendors all workspace crates that lockfile lists — keep src filtered, but include that Cargo.lock.
Case 3: Native libs (openssl, pkg-config)
# openssl-agent.nix
{ rustPlatform, pkg-config, openssl, lib }:
rustPlatform.buildRustPackage {
pname = "desk-agent";
version = "0.1.0";
src = lib.cleanSource ./.;
cargoHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
nativeBuildInputs = [ pkg-config ];
buildInputs = [ openssl ];
strictDeps = true;
}pkg-config is native. openssl is linked. Same split as CMake. rustc should not appear in nix path-info -Shr result.
Case 4: Tests
buildRustPackage runs cargo test by default. Disable only with a reason:
{
doCheck = true; # default: cargo test
# cargoTestFlags = [ "--offline" ];
# checkFlags = [ "--skip=needs_network" ];
# doCheck = false; # only if upstream tests need network — see passthru.tests
}Hermetic tests stay on. A test that hits crates.io or GitHub fails the sandbox — good. Skip one test with checkFlags; do not disable the suite to hide a missing buildInputs.
Case 5: Crane (optional)
Crane builds deps as their own derivations so a one-line src change does not rebuild the world of crates. Use it when CI of a large workspace is the pain. It is a flake input (github:ipetkov/crane) with follows on nixpkgs 26.05.
Do not start a 200-line hello crate on Crane. Measure nix-build first.
fenix / oxalica rust-overlay: pin a nightly or a rustc newer than 26.05. The desk default is pkgs.rustPlatform from the channel.
The trap
The trap is cargoSha256. Deprecated. cargoHash (SRI) or cargoLock, not both.
The other trap is no Cargo.lock in git because “it’s a bin.” Then every CI run resolves crates.io differently. Commit the lock.
src = ./.; including target/ will bust hashes. cleanSource / fileset; add /target to .gitignore.
A third: buildAndTestSubdir without the workspace Cargo.lock in src. A fourth: rust-overlay “because nightly” for a crate that 26.05’s rustc already builds.
The boring rule
buildRustPackage+cargoHash+ committedCargo.lock. NotcargoSha256.- Probe the hash; never leave
fakeHashon main. - Workspace:
buildAndTestSubdir+ lockfile atcargoRoot. pkg-confignative; C libs inbuildInputs;strictDepswhen mixed.- Crane / rust-overlay only when measured.
- Filter
target/out ofsrc. rustc not in the runtime closure.
Try this
- Tiny crate;
fakeHash; pastegot;nix-build; run the bin. - Add a crates.io dep; bump
cargoHash. doCheck = true(default); add a#[test]thatassert!(true).nix path-info -Shr result— rustc should not be in the runtime closure.- Add a git crate to
Cargo.toml, switch Case 1 tocargoLock+outputHashes, probe that hash.