Skip to content

Instantly share code, notes, and snippets.

@kyokley
Last active August 13, 2025 12:02
Show Gist options
  • Save kyokley/03e6a4e21f29171e0df2c3d51bfc47ce to your computer and use it in GitHub Desktop.
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";
uv2nix.url = "github:pyproject-nix/uv2nix";
pyproject-build-systems.url = "github:pyproject-nix/build-system-pkgs";
# Ensure consistent dependencies between these tools
pyproject-nix.inputs.nixpkgs.follows = "nixpkgs";
uv2nix.inputs.nixpkgs.follows = "nixpkgs";
pyproject-build-systems.inputs.nixpkgs.follows = "nixpkgs";
uv2nix.inputs.pyproject-nix.follows = "pyproject-nix";
pyproject-build-systems.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 {
pname = thisProjectAsNixPkg.pname;
version = thisProjectAsNixPkg.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