Nx plugin for running uv workflows in Nx monorepos.
Nx provides a consistent task graph, caching, and affected-based CI so large repos run faster and more predictably. A common operational gap is that Python workflows powered by uv often stay in separate shell scripts and one-off CI jobs, so they miss the same build/test orchestration used by the rest of the monorepo.
@mgwilt/nx-uv bridges that gap by turning common uv workflows into Nx-native targets and adding inference/generators so teams can standardize command execution, reduce bespoke pipeline logic, and manage Python alongside other stacks in a single CI model instead of maintaining a parallel toolchain path.
- Run uv commands through Nx targets instead of ad-hoc shell scripts.
- Infer useful uv targets from existing
pyproject.tomlfiles. - Scaffold uv-ready Python projects and workspace config.
- Keep Python work in the same task graph, caching, and CI flow as the rest of your monorepo.
- Executors for uv command families:
@mgwilt/nx-uv:project@mgwilt/nx-uv:uv@mgwilt/nx-uv:pip@mgwilt/nx-uv:tool@mgwilt/nx-uv:python@mgwilt/nx-uv:auth@mgwilt/nx-uv:cache@mgwilt/nx-uv:self@mgwilt/nx-uv:studio
- Generators:
@mgwilt/nx-uv:workspace@mgwilt/nx-uv:project@mgwilt/nx-uv:integration@mgwilt/nx-uv:convert
- Nx plugin inference (
createNodesV2) forpyproject.toml.
Prerequisites:
Install the plugin:
pnpm add -D @mgwilt/nx-uvLaunch the interactive monorepo manager directly with pnpm dlx (recommended):
pnpm dlx -p @mgwilt/nx-uv nx-uv tuiShorthand (when supported by your pnpm version/environment):
pnpm dlx @mgwilt/nx-uv tuiThe studio provides:
- Workspace setup and inference configuration
- Python app/lib/script generation
- Integration template scaffolding
- Convert existing projects to nx-uv defaults
- Nx target execution across all projects
- Ad-hoc
uvcommand execution - Preview-before-apply flow for mutating actions
Optional flags:
--cwd=<path>scan and manage a workspace from a specific directory--readonlydisable mutation/apply operations--initial-view=<dashboard|workspace|project|integration|convert|inference|tasks|uv>
If plugin inference is configured with includeGlobalTargets: true and a root uv workspace exists, the root inferred project also gets a <targetPrefix>tui target (for example uv:tui).
This walkthrough creates a new Nx monorepo and uses this plugin to scaffold a working uv + Python example.
- Create a new workspace:
pnpm create nx-workspace@latest acme-monorepo --preset=ts --packageManager=pnpm --nxCloud=skip --interactive=false
cd acme-monorepo- Install this plugin and initialize workspace-level uv config:
pnpm add -D @mgwilt/nx-uv
pnpm nx g @mgwilt/nx-uv:workspace --name=acme --membersGlob='packages/py/*'- Generate a Python app and scaffold integrations:
pnpm nx g @mgwilt/nx-uv:project api --projectType=app --directory=packages/py
pnpm nx g @mgwilt/nx-uv:integration --template=fastapi --project=api
pnpm nx g @mgwilt/nx-uv:integration --template=githubExpected file tree (key files) after step 3:
acme-monorepo/
├── nx.json
├── package.json
├── pyproject.toml
├── .github/
│ └── workflows/
│ └── uv-ci.yml
└── packages/
└── py/
└── api/
├── README.md
├── pyproject.toml
├── main.py
├── Dockerfile.fastapi
├── src/
│ └── api/
│ ├── __init__.py
│ └── main.py
└── tests/
└── test_smoke.py
- Add runtime and dev dependencies with uv:
cd packages/py/api
uv add fastapi uvicorn
uv add --dev pytest ruff
cd ../../..- Run plugin-backed targets via Nx:
pnpm nx run api:sync
pnpm nx run api:uv
pnpm nx run api:run
pnpm nx run api:test
pnpm nx run api:buildAt this point you have a working monorepo with:
- Root
pyproject.tomland uv workspace members - A generated
apiPython project underpackages/py/api - Generated FastAPI starter files and a GitHub Actions Nx-first CI template (
test,lint,typecheck,build) - Nx targets that run uv commands consistently in CI/local dev
- Add the plugin to
nx.json:
{
"plugins": [
{
"plugin": "@mgwilt/nx-uv",
"options": {
"targetPrefix": "uv:",
"inferencePreset": "standard",
"includeGlobalTargets": false
}
}
]
}- (Optional, recommended for new repos) Initialize uv workspace config:
pnpm nx g @mgwilt/nx-uv:workspace --name=acmepnpm nx g @mgwilt/nx-uv:project services/api --projectType=app- Run uv-backed targets:
pnpm nx run api:sync
pnpm nx run api:test
pnpm nx run api:buildExisting Python projects
If your repo already has pyproject.toml files, the plugin can infer targets (for example uv:sync, uv:run, uv:test) based on your configured targetPrefix and inference preset.
You can disable or override inferred targets in plugin options:
{
"plugins": [
{
"plugin": "@mgwilt/nx-uv",
"options": {
"targetPrefix": "uv:",
"inferencePreset": "standard",
"inferredTargets": {
"test": false,
"lint": {
"command": "run",
"commandArgs": ["--", "ruff", "check", "src"]
}
}
}
}
]
}Supported inferred target keys are: sync, run, lock, test, lint, build, tree, export, format, venv, publish.
Use integration templates to scaffold common uv ecosystem files for CI, containers, dependency automation, and notebook workflows.
--project=<name>sets<baseDir>to that Nx project's root.--directory=<path>sets<baseDir>to that directory relative to workspace root.- If neither is set,
<baseDir>defaults to workspace root (.). - Workspace-level templates always write to repo root, even if
--projector--directoryis provided. - Workspace-level templates are:
githubgitlabdependency-botspre-commit
| Template | Best for |
|---|---|
github |
GitHub Actions Nx-first CI starter using nx affected |
gitlab |
GitLab CI/CD Nx-first CI starter using nx run-many |
dependency-bots |
Automated dependency update workflows with Renovate and Dependabot |
pre-commit |
Local code quality hooks for uv projects using pre-commit |
| Template | Best for |
|---|---|
fastapi |
Bootstrapping a FastAPI service with uv |
docker |
Containerizing a uv project with Docker |
aws-lambda |
Packaging uv-based AWS Lambda workloads |
| Template | Best for |
|---|---|
jupyter |
Registering a uv-managed Jupyter kernel |
marimo |
Starting marimo notebook workflows |
pytorch |
Configuring PyTorch for CPU/CUDA/ROCm, notebook workflows, and NVIDIA containerized inference |
coiled |
Starting distributed Python experiments with Coiled |
| Template | Best for |
|---|---|
alternative-indexes |
Defining custom/internal Python indexes |
pnpm nx g @mgwilt/nx-uv:integration --template=github
pnpm nx g @mgwilt/nx-uv:integration --template=gitlab
pnpm nx g @mgwilt/nx-uv:integration --template=dependency-bots
pnpm nx g @mgwilt/nx-uv:integration --template=pre-commitpnpm nx g @mgwilt/nx-uv:integration --template=fastapi --project=api
pnpm nx g @mgwilt/nx-uv:integration --template=docker --project=api
pnpm nx g @mgwilt/nx-uv:integration --template=aws-lambda --project=apipnpm nx g @mgwilt/nx-uv:integration --template=jupyter --project=api
pnpm nx g @mgwilt/nx-uv:integration --template=marimo --project=api
pnpm nx g @mgwilt/nx-uv:integration --template=pytorch --project=api
pnpm nx g @mgwilt/nx-uv:integration --template=pytorch --project=api --backend=rocm --includeDocker=false
pnpm nx g @mgwilt/nx-uv:integration --template=pytorch --project=api --backend=cpu --includeNotebook=false --includeDocker=false
pnpm nx g @mgwilt/nx-uv:integration --template=coiled --project=apipnpm nx g @mgwilt/nx-uv:integration --template=alternative-indexes --project=api--overwrite=truereplaces existing files instead of skipping them.--directory=<path>writes baseDir-aware templates into a non-project directory.--skipFormat=trueskips formatter execution after generation.--backend=<cuda|rocm|cpu>applies totemplate=pytorch(default:cuda).--includeNotebook=<true|false>applies totemplate=pytorch(default:true).--includeDocker=<true|false>applies totemplate=pytorch(default:trueforcuda,falsefor other backends).- Prefer
--projectfor app/lib scaffolds and no location flag for workspace-level templates.
- Templates are starter scaffolds; you should review and harden generated files for production use.
- Running the same template multiple times without
--overwrite=trueleaves existing files unchanged. - Workspace-level templates are intentionally global and may affect repository-wide automation.
template=pytorchnow defaults to--backend=cudaand emits CUDA-oriented snippets by default.- NVIDIA inference Docker/Compose assets from
template=pytorchare generated only for--backend=cuda.
- Runtime uv compatibility checks verify that
uvis available and executable. uv 0.9.xand0.10.xare currently the tested ranges. Other versions log a warning and continue.
If quality:ci fails before running tasks with worker/bootstrap errors (for example Failed to load ... Nx plugin(s)), run:
pnpm quality:bootstrap
pnpm nx reset
pnpm quality:ciIf bootstrap still fails, run direct checks to isolate whether the issue is Nx plugin startup or project logic:
pnpm quality:fallback
pnpm --version
pnpm nx --version
node -vThe e2e suite executes a temporary shim binary from the local filesystem. In restricted environments (for example noexec mounts or sandboxed process policies), e2e tests may be skipped when executable spawning is not permitted.
In CI, an all-skipped e2e run is treated as a failure by default. Set NX_UV_ALLOW_E2E_ALL_SKIPPED=1 only in known restricted CI environments where executable shim spawning is intentionally blocked.
This repository publishes LLM-friendly docs artifacts:
llms.txt: concise index of high-signal project docs.llms-full.txt: expanded inline content for the same curated sources.
Regenerate and validate them with:
pnpm llms:generate
pnpm llms:check