Zig2nix: nix flake for packaging, building and running Zig projects

Hello I created flake for bridging zig to nix.


  • automatically updated zig compiler builds
  • .zon file conversion to .json format (zon2json binary, fromZON function)
  • build.zig.zon conversion to a lock file describing all zig package manager artifacts and hashes recursively (zon2json-lock binary)
  • lock file conversion to nix derivation (zon2nix binary, deriveLockFile function). Note the zon2nix binary is not the same as nix-community/zon2nix.
  • provides zig-env that can setup a common runtime for you (x11, wayland, opengl, vulkan) where programs dlopen these libraries
  • provides packageForTarget and package functions that fully integrate with build.zig.zon projects. By default they produce binaries compatible with nix environment, but can be used to build for foreign environments as well.
  • tool for dumping external dependencies of a zig project (system libraries, frameworks) nix run github:Cloudef/zig2nix#deps.master
  • zig2nix project templates
  • and maybe more …

Eventually I plan to merge/rewrite the GitHub - Cloudef/nix-zig-stdenv: cross-compile nixpkgs with zig into this project as well, allowing cross-compiling nixpkgs using zig.

A child project also exists called mach-flake, that flakes the Mach Engine https://machengine.org/.
GitHub - Cloudef/mach-flake: Flake that allows you to get started with Mach engine quickly.


Cross compiling and using nixpkgs from binary cache for target is now supported. All flake platforms are tested. At some point I may add support for cross-compiling nixpkgs using zig itself (tracking issue https://github.com/Cloudef/zig2nix/issues/4)

1 Like

I added ability to print external dependencies of a zig project (system libs and frameworks). Zig does not have separation of system/external and local for frameworks, so it may show frameworks that aren’t external. However it’s still good tool if nixing random zig projects.

Introducing experimental feature that allows you to (cross)compile nixpkgs using zig!
nix build github:Cloudef/zig2nix#zigCross.x86_64-windows-gnu.zlib

Note that compiling to macos has some problems right now Cross-compiling to macos or building inside nix sandbox fails to find headers from -isystem path · Issue #18571 · ziglang/zig · GitHub

Thanks for this. My flake-foo is still bad, could you provide some sample of such a nix flake where SDL2 is available? I keep getting missing headers with @cImport("SDL2/SDL.h") having this flake:

  description = "Zig project flake (Sierpinski triangle with SDL2)";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs?ref=23.11";
    zig2nix.url = "github:Cloudef/zig2nix";

  outputs = { zig2nix, nixpkgs, ... }: let
    flake-utils = zig2nix.inputs.flake-utils;
  in (flake-utils.lib.eachDefaultSystem (system: let
      # Zig flake helper
      # Check the flake.nix in zig2nix project for more options:
      # <https://github.com/Cloudef/zig2nix/blob/master/flake.nix>
    env = zig2nix.outputs.zig-env.${system} {
      customRuntimeLibs = [
        #nixpkgs.SDL2  # nixpkgs.SDL2 not available...
    system-triple = env.lib.zigTripleFromString system;
    in with builtins; with env.lib; with env.pkgs.lib; rec {
      # nix build .#target.{zig-target}
      # e.g. nix build .#target.x86_64-linux-gnu
      packages.target = genAttrs allTargetTriples (target: env.packageForTarget target ({
        src = cleanSource ./.;

        nativeBuildInputs = with env.pkgs; [
        buildInputs = with env.pkgsForTarget target; [

        # Smaller binaries and avoids shipping glibc.
        zigPreferMusl = true;

        # This disables LD_LIBRARY_PATH mangling, binary patching etc...
        # The package won't be usable inside nix.
        zigDisableWrap = true;
      } // optionalAttrs (!pathExists ./build.zig.zon) {
        pname = "sierpinski";
        version = "0.0.1";

      # nix build .
      packages.default = packages.target.${system-triple}.override {
        # Prefer nix friendly settings.
        zigPreferMusl = false;
        zigDisableWrap = false;

      # For bundling with nix bundle for running outside of nix
      # example: https://github.com/ralismark/nix-appimage
      apps.bundle.target = genAttrs allTargetTriples (target: let
        pkg = packages.target.${target};
      in {
        type = "app";
        program = "${pkg}/bin/default";

      # default bundle
      apps.bundle.default = apps.bundle.target.${system-triple};

      # nix run .
      apps.default = env.app [] "zig build run -- \"$@\"";

      # nix run .#build
      apps.build = env.app [] "zig build \"$@\"";

      # nix run .#test
      apps.test = env.app [] "zig build test -- \"$@\"";

      # nix run .#docs
      apps.docs = env.app [] "zig build docs -- \"$@\"";

      # nix run .#deps
      apps.deps = env.showExternalDeps;

      # nix run .#zon2json
      apps.zon2json = env.app [env.zon2json] "zon2json \"$@\"";

      # nix run .#zon2json-lock
      apps.zon2json-lock = env.app [env.zon2json-lock] "zon2json-lock \"$@\"";

      # nix run .#zon2nix
      apps.zon2nix = env.app [env.zon2nix] "zon2nix \"$@\"";

      # nix develop
      devShells.default = env.mkShell {};

Hello, for packaging add pkg-config to nativeBuildInputs, you need the SDL deps only in buildInputs. Add pkg-config to customRuntimeDeps and SDL to customRuntimeLibs if you want zig build run etc to work in dev shell as well nix run .#build and friends … (general development, not packaging)

E.g. this is from one of my projects

zig-env = zig2nix.outputs.zig-env.${system} {
    zig = zig2nix.outputs.packages.${system}.zig.master.bin;
    customRuntimeDeps = with pkgs; [ pkg-config ];
    customRuntimeLibs = [ donutdb ];

FYI to get pkgs you need to do pkgs = nixpkgs.outputs.legacyPackages.${system};

Ah, OK thank you, this works now :slight_smile:

Inspired by GitHub Actions: mlugg/setup-zig
To have good caching on github actions for your zig2nix projects, you can use these 2 actions:

- uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main

The first one installs nix, the second one caches the /nix/store.
The action automatically avoids caching anything that’s already cached in the official binary cache of nixpkgs.
All build.zig.zon dependencies will be cached as well if you use zig2nix’s package functions.

The caching can be seen in in action for example in the zig2nix repository itself:

One caveat is this currently caches even small outputs, which I’ve filled a issue:
PR fixing it https://github.com/DeterminateSystems/magic-nix-cache/pull/69

1 Like