git clone https://github.com/diegosouzapw/awesome-omni-skill
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/tools/uv2nix" ~/.claude/skills/diegosouzapw-awesome-omni-skill-uv2nix && rm -rf "$T"
skills/tools/uv2nix/SKILL.mduv2nix Skill
A specialized skill for converting Python projects using uv to Nix expressions, enabling declarative and reproducible Python package management with Nix.
Skill Overview
Purpose: Provide comprehensive support for using uv2nix to generate Nix derivations from uv workspaces, manage Python dependencies, and integrate Python projects with Nix/NixOS.
Invoke When:
- Converting uv Python projects to Nix
- Creating Nix derivations for Python applications
- Managing Python dependencies with Nix
- Building reproducible Python environments
- Setting up development environments for Python projects
- Packaging Python tools for NixOS
- Working with Python workspaces and monorepos
Core Capabilities
1. What is uv2nix?
uv2nix takes a uv workspace and generates Nix derivations dynamically using pure Nix code. It bridges the gap between uv (Astral's fast Python package manager) and Nix.
Key Benefits:
- Fast: Leverages uv's Rust-based speed
- Reproducible: Nix ensures deterministic builds
- Declarative: Everything in configuration
- Workspace Support: Handles monorepos and multi-package projects
- Modern: Uses PEP-621 pyproject.toml standards
Built on pyproject.nix:
- Uses pyproject.nix infrastructure
- Supports PEP-621 compliant pyproject.toml
- Integrates with nixpkgs Python ecosystem
2. Prerequisites
Install uv (if not already installed)
# Via curl curl -LsSf https://astral.sh/uv/install.sh | sh # Via nix nix-env -iA nixpkgs.uv # Or in NixOS configuration environment.systemPackages = [ pkgs.uv ];
Install uv2nix
# Via nix nix-env -f '<nixpkgs>' -iA uv2nix # Or from flake nix profile install github:pyproject-nix/uv2nix # Verify uv2nix --version
On NixOS
# /etc/nixos/configuration.nix { pkgs, ... }: { environment.systemPackages = with pkgs; [ uv uv2nix ]; }
Via home-manager
home.packages = with pkgs; [ uv uv2nix ];
3. Basic Usage
Initialize uv Project
# Create new Python project with uv uv init my-python-app cd my-python-app # Add dependencies uv add requests flask # Generate lock file uv lock # Project structure: # my-python-app/ # ├── pyproject.toml # ├── uv.lock # └── src/ # └── my_python_app/ # └── __init__.py
Generate Nix Expressions
# Generate Nix derivations from uv workspace uv2nix # This creates/updates: # - default.nix (or checks flake.nix) # - Nix expressions for the workspace
Build with Nix
# Build the package nix-build # Or with flakes nix build # Run the application ./result/bin/my-python-app
4. Project Structure
pyproject.toml - Project metadata:
[project] name = "my-python-app" version = "0.1.0" description = "My Python application" requires-python = ">=3.11" dependencies = [ "requests>=2.31.0", "flask>=3.0.0", ] [project.scripts] my-app = "my_python_app.main:main" [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.uv] dev-dependencies = [ "pytest>=7.4.0", "black>=23.0.0", "ruff>=0.1.0", ]
uv.lock - Lock file with pinned versions:
# Generated by uv # Contains exact dependency versions # Should be committed to version control
5. Workspace Support
Monorepo Structure
# Create workspace mkdir my-workspace cd my-workspace # Initialize workspace cat > pyproject.toml <<EOF [tool.uv.workspace] members = ["packages/*"] EOF # Create packages mkdir -p packages/lib packages/app # Package 1: Library cd packages/lib uv init cat > pyproject.toml <<EOF [project] name = "my-lib" version = "0.1.0" EOF # Package 2: Application cd ../app uv init cat > pyproject.toml <<EOF [project] name = "my-app" version = "0.1.0" dependencies = ["my-lib"] EOF cd ../.. uv lock
Generate Nix for Workspace
# At workspace root uv2nix # Generates derivations for all workspace members
6. Flake Integration
flake.nix for Python Project
{ description = "My Python application using uv2nix"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11"; pyproject-nix = { url = "github:pyproject-nix/pyproject.nix"; inputs.nixpkgs.follows = "nixpkgs"; }; uv2nix = { url = "github:pyproject-nix/uv2nix"; inputs.nixpkgs.follows = "nixpkgs"; inputs.pyproject-nix.follows = "pyproject-nix"; }; }; outputs = { self, nixpkgs, pyproject-nix, uv2nix }: let forAllSystems = nixpkgs.lib.genAttrs [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; in { packages = forAllSystems (system: let pkgs = nixpkgs.legacyPackages.${system}; # Load workspace workspace = uv2nix.lib.${system}.loadWorkspace { workspaceRoot = ./.; }; # Create Python environment pythonSet = pkgs.callPackage pyproject-nix.build.packages { python = pkgs.python311; }; # Build application app = pythonSet.mkPyprojectApplication { inherit workspace; }; in { default = app; my-python-app = app; } ); devShells = forAllSystems (system: let pkgs = nixpkgs.legacyPackages.${system}; workspace = uv2nix.lib.${system}.loadWorkspace { workspaceRoot = ./.; }; pythonEnv = pkgs.python311.withPackages (ps: uv2nix.lib.${system}.getWorkspacePackages workspace ps ); in { default = pkgs.mkShell { packages = [ pythonEnv pkgs.uv ]; shellHook = '' echo "Python development environment" echo "Python: $(python --version)" echo "uv: $(uv --version)" ''; }; } ); apps = forAllSystems (system: { default = { type = "app"; program = "${self.packages.${system}.default}/bin/my-app"; }; }); }; }
Usage:
# Build nix build # Run nix run # Development shell nix develop # Update dependencies uv lock nix flake lock --update-input uv2nix
7. Development Environment
Simple Shell
# shell.nix { pkgs ? import <nixpkgs> {} }: let # Load uv2nix uv2nix = pkgs.callPackage (pkgs.fetchFromGitHub { owner = "pyproject-nix"; repo = "uv2nix"; rev = "main"; sha256 = "..."; }) {}; workspace = uv2nix.lib.loadWorkspace { workspaceRoot = ./.; }; pythonEnv = pkgs.python311.withPackages (ps: uv2nix.lib.getWorkspacePackages workspace ps ); in pkgs.mkShell { packages = [ pythonEnv pkgs.uv pkgs.ruff pkgs.black ]; shellHook = '' echo "Python development environment ready" python --version ''; }
Enhanced Development Shell
{ pkgs ? import <nixpkgs> {} }: let workspace = /* load workspace */; pythonEnv = /* create environment */; in pkgs.mkShell { packages = [ pythonEnv pkgs.uv # Linting and formatting pkgs.ruff pkgs.black pkgs.mypy # Testing pkgs.python311Packages.pytest pkgs.python311Packages.pytest-cov # Development tools pkgs.python311Packages.ipython pkgs.python311Packages.ipdb ]; shellHook = '' # Set up development environment export PYTHONPATH="$PWD/src:$PYTHONPATH" export UV_CACHE_DIR="$PWD/.uv-cache" # Create virtual environment if needed if [ ! -d .venv ]; then uv venv fi source .venv/bin/activate echo "🐍 Python development environment" echo "Python: $(python --version)" echo "uv: $(uv --version)" echo "" echo "Available commands:" echo " uv add <package> - Add dependency" echo " uv lock - Update lock file" echo " pytest - Run tests" echo " ruff check . - Lint code" echo " black . - Format code" ''; }
8. Building Applications
CLI Application
# pyproject.toml [project] name = "my-cli" version = "1.0.0" description = "My CLI tool" [project.scripts] my-cli = "my_cli.main:cli" dependencies = [ "click>=8.0.0", "rich>=13.0.0", ]
Build with Nix:
{ pkgs, workspace }: pkgs.python311Packages.buildPythonApplication { pname = "my-cli"; version = "1.0.0"; src = ./.; propagatedBuildInputs = with pkgs.python311Packages; [ click rich ]; # From workspace pyproject = true; meta = { description = "My CLI tool"; license = pkgs.lib.licenses.mit; mainProgram = "my-cli"; }; }
Web Application (Flask)
[project] name = "my-webapp" version = "1.0.0" dependencies = [ "flask>=3.0.0", "gunicorn>=21.0.0", ] [project.scripts] my-webapp = "my_webapp.app:main"
NixOS Service:
{ config, lib, pkgs, ... }: let myWebapp = /* build from uv2nix */; in { systemd.services.my-webapp = { description = "My Python Web Application"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { ExecStart = "${myWebapp}/bin/gunicorn -b 0.0.0.0:8000 my_webapp.app:app"; Restart = "on-failure"; DynamicUser = true; # Security hardening ProtectSystem = "strict"; NoNewPrivileges = true; PrivateTmp = true; }; environment = { PYTHONUNBUFFERED = "1"; FLASK_ENV = "production"; }; }; networking.firewall.allowedTCPPorts = [ 8000 ]; }
9. Testing Integration
Test Configuration
# pyproject.toml [tool.uv] dev-dependencies = [ "pytest>=7.4.0", "pytest-cov>=4.1.0", "pytest-asyncio>=0.21.0", ] [tool.pytest.ini_options] testpaths = ["tests"] pythonpath = ["src"]
Run Tests in Nix
{ pkgs, workspace }: let pythonEnv = /* build environment with dev deps */; in pkgs.stdenv.mkDerivation { name = "my-app-tests"; src = ./.; buildInputs = [ pythonEnv ]; checkPhase = '' pytest tests/ -v --cov=src ''; installPhase = '' mkdir -p $out echo "Tests passed" > $out/result ''; }
CI Integration
# .github/workflows/test.yml name: Test on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: cachix/install-nix-action@v22 - uses: cachix/cachix-action@v12 with: name: pyproject-nix - run: nix build .#checks.x86_64-linux.default
10. Overlay Creation
Custom Overlay
# overlay.nix final: prev: { pythonPackagesExtensions = prev.pythonPackagesExtensions ++ [ (python-final: python-prev: { my-package = python-final.buildPythonPackage { pname = "my-package"; version = "1.0.0"; src = ./.; propagatedBuildInputs = with python-final; [ requests flask ]; pyproject = true; }; }) ]; }
Use Overlay
{ pkgs ? import <nixpkgs> { overlays = [ (import ./overlay.nix) ]; } }: { my-app = pkgs.python311Packages.my-package; }
11. Common Patterns
Pattern 1: Simple Python Package
# Create project uv init my-package cd my-package # Add dependencies uv add requests click # Lock dependencies uv lock # Generate Nix expressions uv2nix # Build nix-build # Install nix-env -f default.nix -iA package
Pattern 2: Python Library
# pyproject.toml [project] name = "my-lib" version = "0.1.0" description = "My Python library" dependencies = [ "numpy>=1.24.0", "pandas>=2.0.0", ] [build-system] requires = ["setuptools>=68.0"] build-backend = "setuptools.build_meta"
Pattern 3: Data Science Project
# Initialize with scientific packages uv init data-analysis cd data-analysis uv add numpy pandas matplotlib scipy jupyter # Lock uv lock # Generate Nix with development deps
# shell.nix for data science { pkgs ? import <nixpkgs> {} }: let pythonEnv = pkgs.python311.withPackages (ps: with ps; [ numpy pandas matplotlib scipy jupyter ipython scikit-learn ]); in pkgs.mkShell { packages = [ pythonEnv pkgs.uv ]; shellHook = '' jupyter notebook ''; }
Pattern 4: FastAPI Application
[project] name = "my-api" version = "1.0.0" dependencies = [ "fastapi>=0.104.0", "uvicorn[standard]>=0.24.0", "pydantic>=2.0.0", ] [project.scripts] my-api = "my_api.main:run"
NixOS Service:
systemd.services.my-api = { serviceConfig = { ExecStart = "${myApi}/bin/uvicorn my_api.main:app --host 0.0.0.0 --port 8000"; DynamicUser = true; }; };
Pattern 5: Django Project
uv init my-django-app cd my-django-app uv add django psycopg2-binary gunicorn # Lock uv lock
# NixOS module { config, pkgs, ... }: let djangoApp = /* build with uv2nix */; in { services.postgresql = { enable = true; ensureDatabases = [ "myapp" ]; }; systemd.services.django = { serviceConfig = { ExecStart = "${djangoApp}/bin/gunicorn myapp.wsgi:application"; Environment = [ "DJANGO_SETTINGS_MODULE=myapp.settings" "DATABASE_URL=postgresql:///myapp" ]; }; }; }
12. Dependency Management
Adding Dependencies
# Add runtime dependency uv add requests # Add with version constraint uv add "flask>=3.0.0,<4.0.0" # Add development dependency uv add --dev pytest black ruff # Add optional dependency group uv add --group docs sphinx sphinx-rtd-theme # Lock after changes uv lock
Removing Dependencies
# Remove dependency uv remove requests # Lock uv lock
Updating Dependencies
# Update all dependencies uv lock --upgrade # Update specific package uv lock --upgrade-package requests # Check outdated uv pip list --outdated
13. NixOS Integration
System Package
# /etc/nixos/configuration.nix { config, pkgs, ... }: let myPythonApp = import /path/to/app { inherit pkgs; }; in { environment.systemPackages = [ myPythonApp ]; }
User Package (home-manager)
# home.nix { pkgs, ... }: let myTool = import /path/to/tool { inherit pkgs; }; in { home.packages = [ myTool ]; }
Custom Module
# modules/my-python-service.nix { config, lib, pkgs, ... }: with lib; let cfg = config.services.my-python-service; myApp = import ../python-app { inherit pkgs; }; in { options.services.my-python-service = { enable = mkEnableOption "My Python Service"; port = mkOption { type = types.int; default = 8000; }; workers = mkOption { type = types.int; default = 4; }; }; config = mkIf cfg.enable { systemd.services.my-python-service = { description = "My Python Service"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { ExecStart = '' ${myApp}/bin/gunicorn \ -w ${toString cfg.workers} \ -b 0.0.0.0:${toString cfg.port} \ app:application ''; Restart = "on-failure"; DynamicUser = true; }; }; networking.firewall.allowedTCPPorts = [ cfg.port ]; }; }
14. Troubleshooting
Issue 1: Lock File Out of Sync
Problem: Changes to pyproject.toml not reflected
Solution:
# Regenerate lock file uv lock # Force update uv lock --upgrade # Regenerate Nix expressions uv2nix
Issue 2: Build Failures
Problem: Package fails to build with Nix
Solution:
# Add missing build dependencies { pkgs, ... }: pkgs.python311Packages.buildPythonPackage { # ... nativeBuildInputs = with pkgs; [ python311Packages.setuptools python311Packages.wheel ]; buildInputs = with pkgs; [ # System dependencies gcc pkg-config openssl ]; }
Issue 3: Import Errors
Problem: Module not found at runtime
Solution:
# Ensure proper Python path { shellHook = '' export PYTHONPATH="$PWD/src:$PYTHONPATH" ''; }
Issue 4: Native Extensions
Problem: Package with C extensions fails
Solution:
# Ensure build tools in environment
buildInputs = with pkgs; [ python311Packages.setuptools gcc python311.pkgs.cython ];
Issue 5: Workspace Resolution
Problem: Workspace packages not found
Solution:
# Ensure proper workspace structure # pyproject.toml at root with: [tool.uv.workspace] members = ["packages/*"] # Regenerate uv lock uv2nix
15. Best Practices
DO ✅
-
Always commit lock files
git add uv.lock git commit -m "Lock dependencies" -
Use PEP-621 pyproject.toml
[project] name = "my-package" version = "0.1.0" requires-python = ">=3.11" -
Pin Python version
[project] requires-python = ">=3.11,<3.12" -
Version control Nix expressions
git add default.nix flake.nix -
Use development dependencies
uv add --dev pytest ruff black -
Test in clean environment
nix-build ./result/bin/my-app -
Document dependencies
[project] dependencies = [ "requests>=2.31.0", # HTTP client "click>=8.0.0", # CLI framework ] -
Use flakes for reproducibility
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11"; -
Leverage workspace features
[tool.uv.workspace] members = ["packages/*"] -
Keep uv and uv2nix updated
uv self update nix profile upgrade uv2nix
DON'T ❌
-
Don't commitpycache
__pycache__/ *.pyc -
Don't skip lock file
# ❌ Bad uv add requests # without uv lock # ✅ Good uv add requests && uv lock -
Don't mix pip and uv
# ❌ Don't use pip install with uv project # ✅ Use uv add -
Don't hardcode paths
# ❌ Bad config_path = "/home/user/config.json" # ✅ Good config_path = os.path.expanduser("~/.config/myapp/config.json") -
Don't commit secrets
# Use environment variables or secret management # Never commit API keys, passwords -
Don't skip tests
# Always run tests before committing pytest tests/ -
Don't ignore warnings
# Address deprecation warnings # Fix type errors
Command Reference
# uv commands uv init [project] # Initialize project uv add <package> # Add dependency uv add --dev <package> # Add dev dependency uv remove <package> # Remove dependency uv lock # Generate lock file uv lock --upgrade # Update all dependencies uv sync # Install dependencies uv run <command> # Run in environment uv tree # Show dependency tree # uv2nix commands uv2nix # Generate Nix expressions uv2nix --help # Show help # Nix commands nix-build # Build package nix-shell # Enter dev environment nix flake init # Initialize flake nix build # Build (flakes) nix develop # Dev shell (flakes) nix run # Run app (flakes)
Success Metrics
- Reproducible: Same pyproject.toml → same build
- Fast: uv's Rust-based speed + Nix caching
- Declarative: Everything in configuration
- Workspace Support: Monorepo capabilities
- Modern: PEP-621 compliant
- Integrated: Works with NixOS services
- Tested: CI integration validated
Ready to build reproducible Python applications with uv2nix! 🐍