askill
uv2nix

uv2nixSafety 100Repository

uv2nix Skill

10 stars
1.2k downloads
Updated 2/6/2026

Package Files

Loading files...
SKILL.md

uv2nix 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 ✅

  1. Always commit lock files

    git add uv.lock
    git commit -m "Lock dependencies"
    
  2. Use PEP-621 pyproject.toml

    [project]
    name = "my-package"
    version = "0.1.0"
    requires-python = ">=3.11"
    
  3. Pin Python version

    [project]
    requires-python = ">=3.11,<3.12"
    
  4. Version control Nix expressions

    git add default.nix flake.nix
    
  5. Use development dependencies

    uv add --dev pytest ruff black
    
  6. Test in clean environment

    nix-build
    ./result/bin/my-app
    
  7. Document dependencies

    [project]
    dependencies = [
        "requests>=2.31.0",  # HTTP client
        "click>=8.0.0",      # CLI framework
    ]
    
  8. Use flakes for reproducibility

    inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
    
  9. Leverage workspace features

    [tool.uv.workspace]
    members = ["packages/*"]
    
  10. Keep uv and uv2nix updated

    uv self update
    nix profile upgrade uv2nix
    

DON'T ❌

  1. Don't commitpycache

    __pycache__/
    *.pyc
    
  2. Don't skip lock file

    # ❌ Bad
    uv add requests  # without uv lock
    
    # ✅ Good
    uv add requests && uv lock
    
  3. Don't mix pip and uv

    # ❌ Don't use pip install with uv project
    # ✅ Use uv add
    
  4. Don't hardcode paths

    # ❌ Bad
    config_path = "/home/user/config.json"
    
    # ✅ Good
    config_path = os.path.expanduser("~/.config/myapp/config.json")
    
  5. Don't commit secrets

    # Use environment variables or secret management
    # Never commit API keys, passwords
    
  6. Don't skip tests

    # Always run tests before committing
    pytest tests/
    
  7. 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! 🐍

Install

Download ZIP
Requires askill CLI v1.0+

AI Quality Score

88/100Analyzed 2/6/2026

A high-quality, comprehensive guide for using uv2nix with Nix. It provides excellent code examples for flakes, shells, and services, though the text is slightly truncated at the end.

100
95
90
85
95

Metadata

Licenseunknown
Version-
Updated2/6/2026
Publisherolafkfreund

Tags

apici-cddatabasegithubgithub-actionslintingobservabilitysecuritytesting