Skip to content

Instantly share code, notes, and snippets.

@kyokley
Last active February 16, 2026 05:27
Show Gist options
  • Select an option

  • Save kyokley/03e6a4e21f29171e0df2c3d51bfc47ce to your computer and use it in GitHub Desktop.

Select an option

Save kyokley/03e6a4e21f29171e0df2c3d51bfc47ce to your computer and use it in GitHub Desktop.
Uv2nix template
{
description = "My Python App with Nix and uv2nix";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
# Core pyproject-nix ecosystem tools
pyproject-nix = {
url = "github:pyproject-nix/pyproject.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
pyproject-build-systems = {
url = "github:pyproject-nix/build-system-pkgs";
inputs.nixpkgs.follows = "nixpkgs";
inputs.pyproject-nix.follows = "pyproject-nix";
};
uv2nix = {
url = "github:pyproject-nix/uv2nix";
inputs.nixpkgs.follows = "nixpkgs";
inputs.pyproject-nix.follows = "pyproject-nix";
};
};
outputs = {
self,
nixpkgs,
flake-utils,
uv2nix,
pyproject-nix,
pyproject-build-systems,
...
}:
flake-utils.lib.eachDefaultSystem (
system: let
pkgs = import nixpkgs {inherit system;};
python = pkgs.python312; # Your desired Python version
# 1. Load Project Workspace (parses pyproject.toml, uv.lock)
workspace = uv2nix.lib.workspace.loadWorkspace {
workspaceRoot = ./.; # Root of your flake/project
};
# 2. Generate Nix Overlay from uv.lock (via workspace)
uvLockedOverlay = workspace.mkPyprojectOverlay {
sourcePreference = "wheel"; # Or "sdist"
};
# 3. Placeholder for Your Custom Package Overrides
myCustomOverrides = final: prev: {
# e.g., some-package = prev.some-package.overridePythonAttrs (...); */
};
# 4. Construct the Final Python Package Set
pythonSet = (pkgs.callPackage pyproject-nix.build.packages {inherit python;})
.overrideScope (nixpkgs.lib.composeManyExtensions [
pyproject-build-systems.overlays.default # For build tools
uvLockedOverlay # Your locked dependencies
myCustomOverrides # Your fixes
]);
# --- This is where your project's metadata is accessed ---
projectNameInToml = "project-name"; # MUST match [project.name] in pyproject.toml!
thisProjectAsNixPkg = pythonSet.${projectNameInToml};
# ---
# 5. Create the Python Runtime Environment
appPythonEnv =
pythonSet.mkVirtualEnv
(thisProjectAsNixPkg.pname + "-env")
workspace.deps.default; # Uses deps from pyproject.toml [project.dependencies]
in {
# Development Shell
devShells.default = pkgs.mkShell {
packages = [appPythonEnv pkgs.ruff pkgs.uv];
shellHook = ''# Your custom shell hooks */ '';
};
# Nix Package for Your Application
packages.default = pkgs.stdenv.mkDerivation {
inherit (thisProjectAsNixPkg) pname version;
src = ./.; # Source of your main script
nativeBuildInputs = [pkgs.makeWrapper];
buildInputs = [appPythonEnv]; # Runtime Python environment
installPhase = ''
mkdir -p $out/bin
cp main.py $out/bin/${thisProjectAsNixPkg.pname}-script
chmod +x $out/bin/${thisProjectAsNixPkg.pname}-script
makeWrapper ${appPythonEnv}/bin/python $out/bin/${thisProjectAsNixPkg.pname} \
--add-flags $out/bin/${thisProjectAsNixPkg.pname}-script
'';
};
packages.${thisProjectAsNixPkg.pname} = self.packages.${system}.default;
# App for `nix run`
apps.default = {
type = "app";
program = "${self.packages.${system}.default}/bin/${thisProjectAsNixPkg.pname}";
};
apps.${thisProjectAsNixPkg.pname} = self.apps.${system}.default;
}
);
}
@kyokley
Copy link
Author

kyokley commented Aug 13, 2025

For anyone that comes across this gist, I don't want to claim this template as my own creation but lost track of where I came across it originally. If you recognize this, contact me so I can add proper attribution

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment