Open-source project
nix-community/fenix avatar
nix-community/fenix

Fenix gives Nix a Rust toolchain, and the overlay trap is in your nixpkgs pin

Rust toolchains and rust-analyzer nightly for Nix [maintainers=@figsoda, @eureka-cpu]

1,132 stars67 forksNixMPL-2.0

At a glance

What is it?
A Nix flake providing the minimal, default and complete rustup profiles, nightly toolchains and a nightly rust-analyzer, intended as a replacement for rustup and the nixpkgs-mozilla overlay. The useful part is the manifest-based toolchain constructors, and the part that will waste your afternoon is that the overlay inherits your own nixpkgs and may miss the binary cache.
Who is it for?
Adopt Fenix if you build Rust on Nix and want a pinned, cacheable toolchain without rustup writing into your home directory. Do not adopt it if you need the toolchain to be identical across machines with different nixpkgs pins, because the overlay uses the nixpkgs from your system and that is where the cache misses come from.
Can I use it commercially?
Yes, with conditions. MPL-2.0 is a weak copyleft licence: you can use it inside commercial and closed-source software, but if you distribute changes to its own files, you must publish those changes under the same licence.
Is it still maintained?
Yes. The repository received new commits within the last day.
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 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A replacement for rustup, with the profiles you already know

Fenix provides the `minimal`, `default` and `complete` profiles of rust toolchains, the `latest` profile of nightly toolchains, a nightly version of rust-analyzer, and its VS Code extension. The profiles link to the rustup concepts documentation, so the naming and the contents of each profile are the same ones you would get from rustup itself.

The stated aim is to be a replacement for rustup and for the rust overlay provided by nixpkgs-mozilla. That is a clear statement of intent, and it captures the two things people actually want: a toolchain that is a Nix derivation, and one that a colleague or a CI runner reproduces exactly.

The primary language is Nix, the licence is MPL-2.0, and the default branch is `main`. The repository entries match the shape of a flake project: `flake.nix` and `flake.lock`, a `default.nix` for the non-flake path, an `overlay.nix` for the overlay use, a `lib/` directory holding the library code, a `data/` directory, an `update` directory for the toolchain update tooling, and a `test` directory.

The maintainers are listed in the repository description as @figsoda and @eureka-cpu, and the last push was on 2026-10-19 with the repository not archived. There are no GitHub releases, so versions are tracked through the flake and the toolchain data rather than through release tags.

One thing the README mentions early is worth pausing on, because it changes what a user should expect from a build. A binary cache is available for `x86_64-darwin`, `aarch64-darwin` and `x86_64-linux` on cachix, and you enable it with a single command.

sh
cachix use fenix

Having the cache is what makes Fenix usable at all. Without it, every toolchain component is a download and a build.

The overlay inherits your nixpkgs, and that is the cache trap

The note about the overlay is the single most useful piece of operational advice in the README, and it is easy to skim past because it sits above the table of contents.

The nixpkgs from your system will be used when fenix is being used as an overlay, which may not be cached if you are using a stable or older version of nixpkgs. The workaround the README offers is to use the packages directly, or to apply the overlay with the nixpkgs that Fenix itself pins.

nix
{
  nixpkgs.overlays = [
    (_: super: let pkgs = fenix.inputs.nixpkgs.legacyPackages.${super.system}; in fenix.overlays.default pkgs pkgs)
  ];
}

The mechanism is worth understanding. An overlay is a function that takes your nixpkgs and returns a new set of packages. Fenix's overlay is written to work with the nixpkgs it was developed against, and when you apply it to a different nixpkgs, the derivations it produces are not the ones sitting in the binary cache. The cache is keyed by derivation hash, so a changed input produces a different hash, and the cache hit becomes a miss.

The fix in the snippet rewrites the overlay application so that Fenix's own nixpkgs are used inside the overlay, while the rest of your system keeps yours. The `super.system` call is the standard way to reach the outer attribute set from inside an overlay, and the `let ... in` binds the inner nixpkgs to what Fenix expects.

The issue tracker entry referenced for this workaround is #79, so this is a known and discussed problem rather than an edge case, and it is worth reading that issue before you pick a path, because the choice between using the packages directly and rewriting the overlay has real consequences for how the rest of your configuration sees Rust.

Three ways to consume it, and the flake is the recommended one

The README offers three usage modes and marks the flake as recommended.

As a flake, the example wires Fenix in as an input, pins the nixpkgs input, and then uses the toolchain in a NixOS configuration.

nix
{
  inputs = {
    fenix = {
      url = "github:nix-community/fenix";
      inputs.nixpkgs.follows = "nixpkgs";
    };
    nixpkgs.url = "nixpkgs/nixos-unstable";
  };

  outputs = { self, fenix, nixpkgs }: {
    packages.x86_64-linux.default = fenix.packages.x86_64-linux.minimal.toolchain;
    nixosConfigurations.nixos = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        ({ pkgs, ... }: {
          nixpkgs.overlays = [ fenix.overlays.default ];
          environment.systemPackages = [
            (pkgs.fenix.complete.withComponents [
              "cargo"
              "clippy"
              "rust-src"
              "rustc"
              "rustfmt"
            ])
            pkgs.rust-analyzer-nightly
          ];
        })
      ];
    };
  };
}

The `inputs.nixpkgs.follows = "nixpkgs"` line is the one that matters. It tells Fenix's own nixpkgs input to follow yours, so there is a single nixpkgs in the flake rather than two, and the overlay problem from the previous section is avoided in the common case.

As a set of packages, without a flake, you import the tarball directly.

nix
let
  fenix = import (fetchTarball "https://github.com/nix-community/fenix/archive/main.tar.gz") { };
in
fenix.minimal.toolchain

As an overlay, in a configuration.nix.

nix
{ pkgs, ... }: {
  nixpkgs.overlays = [
    (import "${fetchTarball "https://github.com/nix-community/fenix/archive/main.tar.gz"}/overlay.nix")
  ];
  environment.systemPackages = with pkgs; [
    (fenix.complete.withComponents [
      "cargo"
      "clippy"
      "rust-src"
      "rustc"
      "rustfmt"
    ])
    rust-analyzer-nightly
  ];
}

The `withComponents` call is the mechanism that makes all three work the same way. It takes a list of component names and produces a derivation containing exactly those, so you can have a build environment with cargo and clippy and rustfmt but without the full toolchain, and you can vary that per project.

The toolchain constructors are where the real power is

The API section explains what a toolchain is in Fenix terms, and the answer is a set of derivations with a function to select among them.

nix
{
  # components
  cargo = <derivation>;
  rustc = <derivation>; # rustc with rust-std
  rustc-unwrapped = <derivation>; # rustc without rust-std, same as rustc.unwrapped
  rustfmt = <derivation>; # alias to rustfmt-preview
  rust-src = <derivation>; # RUST_SRC_PATH = "${fenix.complete.rust-src}/lib/rustlib/src/rust/library"
  # ...

  # derivation with all the components
  toolchain = <derivation>;

  # not available in nightly toolchains
  # derivation with all the components from a profile
  minimalToolchain = <derivation>;
  defaultToolchain = <derivation>;
  completeToolchain = <derivation>;

  # withComponents : [string] -> derivation
  withComponents = <function>;
}

The `toolchainOf` function is the one you will use most, and it takes four arguments with defaults. The `root` is the download root, usually left as the default `https://static.rust-lang.org/dist`. The `channel` is one of stable, beta, nightly, or a version number. The `date` selects a specific toolchain, latest if unset. And the `sha256` is the hash of the manifest, which is required in pure evaluation mode, with the trick being to set it to `lib.fakeSha256` and read the actual hash out of the error message.

nix
toolchainOf {
  channel = "beta";
  date = "2021-08-29";
  sha256 = "0dkmjil9avba6l0l9apmgwa8d0h4f8jzgxkq3gvn8d2xc68ks5a5";
}

There is also a `combine` function, which takes a list of derivations and combines them into one, and the README recommends `withComponents` instead when the components come from the same toolchain.

nix
combine [
  minimal.rustc
  minimal.cargo
  targets.wasm32-unknown-unknown.latest.rust-std
]

That example is worth reading for the cross-target case, because it is how you add a target's standard library to a toolchain that does not have it, which is the usual reason to reach for the low level API at all.

Then there are the manifest based constructors. `fromManifest` creates a toolchain from a rustup manifest, `fromManifestFile` from a manifest file, `fromToolchainFile` from a rust toolchain file such as `rust-toolchain` or `rust-toolchain.toml`, and `fromToolchainName` from a channel, version or date.

nix
fromToolchainFile { dir = ./.; }

The `fromToolchainFile` form is the interesting one for most projects, because it means the `rust-toolchain.toml` that already lives in the repository becomes the single source of truth for the toolchain, and the Nix configuration follows it rather than duplicating the version number.

Maintenance, the monthly branch and the licence

The README has a section on a monthly branch, and it is worth knowing about because it changes how you think about pinning.

A monthly branch exists alongside `main`, and the toolchain data is regenerated rather than hand written. The `update` directory in the repository root is the machinery for that, and the `data/` directory holds the toolchain metadata. A Nix project that tracks a Rust toolchain through a pin file will otherwise need someone to notice when upstream publishes a new version and move the pin, and the monthly branch is the mechanism that does it on a schedule instead.

The licence is MPL-2.0, which is a file-level copyleft licence. For a Nix flake that most projects consume as a pinned input rather than modify, it is a reasonable choice, and it is worth noting that the copyleft applies at the file level, so a project that adds its own overlay on top of Fenix rather than editing Fenix's own files is not thereby obliged to release the overlay.

The repository has a `test` directory, which is the right place for a tool that has to produce correct hashes and correct derivations, and the `flake.lock` pins the inputs so a build is reproducible.

For a reader deciding whether to adopt it, the honest summary is this. Fenix is a well-maintained, actively developed project that solves a real problem, which is that rustup is a stateful thing that does not fit into a declarative, reproducible build. It gives you the toolchain profiles you expect, a nightly rust-analyzer, and constructors that let you pin exactly the toolchain your project declares. The cost is the ecosystem friction of Nix itself, and the one sharp edge the README happens to document clearly, which is that the overlay and your nixpkgs have to agree or you lose the binary cache.

Editorial conclusion

Adopt Fenix if you build Rust on Nix and want a pinned, cacheable toolchain without rustup writing into your home directory. Do not adopt it if you need the toolchain to be identical across machines with different nixpkgs pins, because the overlay uses the nixpkgs from your system and that is where the cache misses come from. Verify first that the toolchain file or manifest you point it at matches the toolchain your project actually expects, that the sha256 you supply is the real one rather than lib.fakeSha256, and that you have read the cache note before you blame the build for being slow.

Frequently asked questions

What does Fenix provide for Nix users?

The minimal, default and complete rustup profiles of Rust toolchains, the latest profile of nightly toolchains, a nightly version of rust-analyzer and its VS Code extension. It aims to replace rustup and the rust overlay from nixpkgs-mozilla.

Why is my Fenix build not hitting the binary cache?

The nixpkgs from your system will be used when Fenix is used as an overlay, which may not be cached if you are on a stable or older nixpkgs. Use the packages directly, or apply the overlay using Fenix's own nixpkgs, as shown in the README workaround referencing issue 79.

How do I pin a specific Rust toolchain in Fenix?

Use the toolchainOf function with a channel such as stable, beta, nightly or a version number, an optional date, and the sha256 of the manifest. In pure evaluation mode you set the hash to lib.fakeSha256 and read the real value from the error message.

Can Fenix read the rust-toolchain.toml from my project?

Yes. The fromToolchainFile function creates a derivation from a rust toolchain file, taking either a file path or a directory containing rust-toolchain or rust-toolchain.toml, so the pin file in the repository can stay the single source of truth.

Where is the binary cache for Fenix and which platforms does it cover?

The binary cache is on cachix and is enabled with cachix use fenix. It is available for x86_64-darwin, aarch64-darwin and x86_64-linux.

Official sources

  1. Issues
  2. License: MPL-2.0
  3. nix-community/fenix on GitHub
  4. README
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/nix-community-fenix.svg)](https://hysenlabs.com/projects/nix-community-fenix)

Was this useful?