| name | nix-package |
| description | Create and maintain Nix packages in the NyxOS flake, including derivations, module wiring, lockfiles, and upstream patterns. Covers general packages and .NET-specific tooling. |
Nix Package Creation Guide
Repo layout
packages/<name>/
default.nix # buildDotnetModule / stdenv.mkDerivation / …
deps.json # NuGet lockfile (dotnet packages only)
update.sh # bump-version helper
modules/
<name>.nix # flake-parts module to expose into the flake
General package skeleton
{ lib, stdenv, fetchFromGitHub, ... }:
stdenv.mkDerivation rec {
pname = "my-tool";
version = "1.2.3";
src = fetchFromGitHub {
owner = "owner";
repo = "my-tool";
rev = "v${version}";
hash = "";
};
# nativeBuildInputs — tools needed at build time (compilers, cmake, …)
# buildInputs — libraries linked into the final binary
# installPhase — copy/generate into $out
meta = with lib; {
description = "Short description";
homepage = "https://github.com/owner/my-tool";
license = licenses.mit;
maintainers = [];
platforms = platforms.linux;
};
}
Resolve hash: nix build .#my-tool 2>&1 | grep "got:" then paste the hash.
.NET packages (buildDotnetModule)
Use for any C#, F#, or VB.NET project. Handles restore, build, publish, and wrapper creation.
{ lib, buildDotnetModule, fetchFromGitHub, dotnetCorePackages, clang }:
buildDotnetModule rec {
pname = "my-dotnet-tool";
version = "1.0.0";
src = fetchFromGitHub {
owner = "owner";
repo = "my-dotnet-tool";
rev = "v${version}";
hash = "";
};
projectFile = "src/MyTool/MyTool.csproj";
nugetDeps = ./deps.json;
dotnet-sdk = dotnetCorePackages.sdk_10_0;
dotnet-runtime = null; # null for NativeAOT / self-contained
selfContainedBuild = true;
dotnetInstallFlags = [ "-p:PublishAot=true" ];
executables = [ "mytool" ];
nativeBuildInputs = [ clang ]; # required for NativeAOT on Linux
meta = with lib; {
description = "...";
homepage = "https://github.com/owner/my-dotnet-tool";
license = licenses.mit;
platforms = [ "x86_64-linux" "aarch64-linux" ];
mainProgram = "mytool";
};
}
Key options
| Option | Purpose | Common values |
|---|
projectFile | Path to .csproj/.fsproj | "src/Foo/Foo.csproj" |
nugetDeps | Lockfile of NuGet dependencies | ./deps.json |
dotnet-sdk | SDK version | dotnetCorePackages.sdk_10_0 |
dotnet-runtime | Runtime to bundle; null for AOT | null or default |
selfContainedBuild | Self-contained (bundles runtime) | true |
dotnetInstallFlags | Extra flags for dotnet publish | [ "-p:PublishAot=true" ] |
executables | Binaries to symlink to $out/bin/ | [ "aspire" ] |
runtimeDeps | Native libs for LD_LIBRARY_PATH | [ pkgs.openssl ] |
NativeAOT on Linux
- Requires
clang in nativeBuildInputs (the .NET ILC compiler spawns clang as linker)
- Use
dotnet-runtime = null (AOT output needs no runtime)
- Use
dotnetInstallFlags = [ "-p:PublishAot=true" ]
- Only
x86_64-linux and aarch64-linux are supported as target platforms
Generating the NuGet lockfile
nix build .#aspire-cli.fetch-deps
./result /path/to/deps.json
The fetch-deps passthru is auto-generated by buildDotnetModule when nugetDeps is a path (file or JSON). It spawns a nix-shell with the build environment, does a dotnet restore, and writes the lockfile.
If nix run fails with "Not a directory", use nix build then run the result symlink directly.
Wiring into the flake
Create a module at modules/<name>.nix:
{ ... }: {
# Expose via nix build .#<name>
perSystem = { pkgs, ... }: {
packages.<name> = pkgs.callPackage ../packages/<name> { };
};
# Optionally install on hosts
flake.modules.nixos.<name> = { pkgs, ... }: {
nixpkgs.overlays = [
(final: prev: {
<name> = final.callPackage ../packages/<name> { };
})
];
environment.systemPackages = [ pkgs.<name> ];
};
}
In host files (modules/hosts/), import by adding to the imports list inside the with inputs.self.modules.nixos scope.
Update workflow
- Bump version in
packages/<name>/default.nix
- Regenerate source hash:
nix-build 2>&1 | grep "got:"
- Regenerate
deps.json via fetch-deps
- Build:
nix build .#<name>
- Commit and rebuild host if auto-installed
Upstreaming to nixpkgs
- Place under
pkgs/by-name/<first2chars>/<pname>/
- Remove flake wiring (no
perSystem or overlay needed)
- Follow nixpkgs
CONTRIBUTING.md
- Run
nix flake check before submitting PR