uv-workspaces
UV Workspaces
Quick reference for managing monorepo and multi-package projects with UV workspaces.
When to Use This Skill
| Use this skill when... | Use another skill instead when... |
|---|---|
| Setting up a Python monorepo with shared deps | Managing a single package (uv-project-management) |
| Configuring workspace members and inter-package deps | Adding git/URL dependencies (uv-advanced-dependencies) |
Using --package or --all-packages flags |
Building/publishing to PyPI (python-packaging) |
| Creating virtual workspaces (root with no project) | Managing Python versions (uv-python-versions) |
| Debugging workspace dependency resolution | Running standalone scripts (uv-run) |
Quick Reference
Workspace Structure
my-workspace/
├── pyproject.toml # Root workspace config
├── uv.lock # Shared lockfile (all members)
├── packages/
│ ├── core/
│ │ ├── pyproject.toml
│ │ └── src/core/
│ ├── api/
│ │ ├── pyproject.toml
│ │ └── src/api/
│ └── cli/
│ ├── pyproject.toml
│ └── src/cli/
└── README.md
Root pyproject.toml
[project]
name = "my-workspace"
version = "0.1.0"
requires-python = ">=3.11"
[tool.uv.workspace]
members = ["packages/*"]
# Optional: exclude specific members
exclude = ["packages/experimental"]
Virtual Workspace (No Root Package)
When the root is purely organizational and not a package itself, omit the [project] table:
# Root pyproject.toml — virtual workspace (no [project] table)
[tool.uv.workspace]
members = ["packages/*"]
- The root is not a workspace member
- All members live in subdirectories
uv runanduv syncrequire--packageor--all-packages(will error without them)
Member pyproject.toml
[project]
name = "my-api"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
"my-core", # Workspace member
"fastapi", # External (PyPI)
]
[tool.uv.sources]
my-core = { workspace = true }
Common Commands
| Operation | Command |
|---|---|
| Sync workspace root | uv sync |
| Sync all members | uv sync --all-packages |
| Sync specific member | uv sync --package my-api |
| Run in member context | uv run --package my-api python script.py |
| Lock entire workspace | uv lock |
| Upgrade a dependency | uv lock --upgrade-package requests |
| Build specific package | uv build --package my-core |
| Add dep to member | cd packages/api && uv add requests |
| Add workspace dep | cd packages/api && uv add ../core |
Key Behaviors
Source Inheritance
Root tool.uv.sources apply to all members unless overridden.
Use case: Define workspace sources in root for all members, override only when a specific member needs a different version:
# Root pyproject.toml — applies to all members
[tool.uv.sources]
my-utils = { workspace = true }
# packages/legacy/pyproject.toml — needs pinned version
[tool.uv.sources]
my-utils = { path = "../utils-v1" } # Overrides root entirely
Override is per-dependency and total — if a member defines a source for a dependency, the root source for that dependency is ignored completely.
requires-python Resolution
The workspace enforces the intersection of all members' requires-python:
# packages/core: requires-python = ">=3.10"
# packages/api: requires-python = ">=3.11"
# Effective: requires-python = ">=3.11"
All members must have compatible Python version requirements.
Editable Installations
Workspace member dependencies are always editable — source changes are immediately available without reinstallation.
Default Scope
| Command | Default scope | Override |
|---|---|---|
uv lock |
Entire workspace | — |
uv sync |
Workspace root only | --package, --all-packages |
uv run |
Workspace root only | --package, --all-packages |
uv build |
All members | --package |
Workspace vs Path Dependencies
| Feature | { workspace = true } |
{ path = "../pkg" } |
|---|---|---|
| Shared lockfile | Yes | No |
| Always editable | Yes | Optional |
| Must be workspace member | Yes | No |
--package flag works |
Yes | No |
| Conflicting deps allowed | No | Yes |
Use path dependencies when members need conflicting requirements or separate virtual environments.
Docker Layer Caching
# Install deps first (cached layer)
COPY pyproject.toml uv.lock packages/*/pyproject.toml ./
RUN uv sync --frozen --no-install-workspace
# Then install project (changes frequently)
COPY . .
RUN uv sync --frozen
| Flag | Effect |
|---|---|
--no-install-project |
Skip current project, install deps only |
--no-install-workspace |
Skip all workspace members, install deps only |
--no-install-package <name> |
Skip specific package(s) |
--frozen |
Skip lockfile freshness check |
Agentic Optimizations
| Context | Command |
|---|---|
| Sync all members | uv sync --all-packages |
| CI sync (frozen) | uv sync --all-packages --frozen |
| Test specific member | uv run --package my-core pytest --dots --bail=1 |
| Test all members | uv run --all-packages pytest --dots --bail=1 |
| Lock with upgrade | uv lock --upgrade-package <dep> |
| Build one package | uv build --package my-core |
| Docker deps layer | uv sync --frozen --no-install-workspace |
See Also
uv-project-management— Managing individual packagesuv-advanced-dependencies— Path and Git dependenciespython-packaging— Building and publishing workspace packages
For detailed workspace patterns (virtual workspaces, Docker integration, source inheritance, syncing strategies), CI/CD examples, and troubleshooting, see REFERENCE.md.