# uv self-updates on one install path, and its Dockerfile pins everything except the bootstrap

> uv is a Rust implementation of pip, pipx, poetry, pyenv, twine, and virtualenv under one command, and the README is explicit that it replaces all seven. Reading the repository rather than the highlights turns up a self-update that exists for only one of three install routes, a build pinned by base image digest and apt snapshot except for the binary it bootstraps from, a workspace crate excluded because it needs nightly, and a 10-100x claim whose conditions live in a separate benchmark file.

**astral-sh/uv** — An extremely fast Python package and project manager written in Rust.

- Repository: https://github.com/astral-sh/uv
- Website: https://docs.astral.sh/uv
- Stars: 90,177 · Forks: 3,613
- Language: Rust
- License: Apache-2.0
- Published: 2026-08-08 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/astral-sh-uv

## uv self update exists for the standalone installer and nothing else

Three install routes are documented and they do not behave the same afterwards. Two are the standalone installers, curl -LsSf https://astral.sh/uv/install.sh | sh on macOS and Linux and a PowerShell one-liner for Windows. Two more come from PyPI, pip install uv and pipx install uv.

Self-update is available for one of them. The README says that if installed via the standalone installer, uv can update itself to the latest version:

```
uv self update
```

A pip or pipx install has no such path, so it is upgraded through the tool that put it there. That makes the install method a property of each machine rather than of the project, and it is invisible once uv is on the PATH.

The consequence is a runbook gap. With releases at 0.12.19 on 2026-09-25, 0.12.20 on 2026-09-28, and 0.12.21 on 2026-09-29, the gap between current and a few patches behind is about four days. A team where some machines self-update and others wait for a pipx run will hold different versions of the same tool, and a bug report will not tell you which machine produced it. Write the install route into the same place you write the pinned version.

## The build pins the base image by digest and the bootstrap binary by latest

The Dockerfile is one of the more carefully pinned build files you will read. The base is pinned to a digest, ubuntu:24.04 with a sha256 on the FROM line. apt is pinned to a dated Ubuntu archive held in an argument, UBUNTU_SNAPSHOT=20260301T000000Z, with a comment explaining that the snapshot is used for reproducibility and that ca-certificates are needed to reach it. rustup is pinned to RUSTUP_VERSION=1.28.1, downloaded over TLS from static.rust-lang.org, and the toolchain itself comes from a committed rust-toolchain.toml rather than from a channel name. There is even a retry stanza for apt, Acquire::Retries set to 3, with a comment about transient 503s from snapshot.ubuntu.com.

Then there is this line:

```
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
```

The compiler that builds uv is copied from a floating latest tag. So the one input in the file that can change without a commit is the tool that produces the artifact, and the artifact does not record which version of it was used.

That does not make the pinning useless, since a digest-pinned base and a snapshot-pinned apt make the system side reproducible. It does mean a rebuild of the same commit at a later date is not the same build, and that is worth knowing before you treat this Dockerfile as a provenance record.

## One workspace crate is excluded because it needs nightly

The Cargo workspace takes every crate with a glob and then carves out two exceptions:

```
members = ["crates/*"]
exclude = [
  "scripts",
  # Needs nightly
  "crates/uv-trampoline",
]
```

The second exclusion is a single crate, uv-trampoline, and the whole explanation is the comment above it. Meanwhile the workspace pins rust-version = "1.96.0" and edition = "2024", so the declared floor is a specific stable release.

For a reader auditing the source, that leaves a directory in the tree the workspace does not build. A grep for a symbol, a change to a shared type, or a security review that assumes the workspace is the whole project will all reach crates/uv-trampoline and find code that no workspace-wide command compiles, with a two-word comment as the only account of why. If you are reviewing this codebase, treat that crate as a separate build with its own toolchain requirement, because the manifest does not describe one.

## requires-python claims 3.8 while the classifiers run through 3.15

The package metadata sets requires-python = ">=3.8" and then lists classifiers for Python 3.8, 3.9, 3.10, 3.11, 3.12, 3.13, 3.14, and 3.15, along with 3 :: Only and Programming Language :: Rust. The development status is 5, Production/Stable, and the audience is developers on an OS Independent console.

So the floor a resolver enforces is eight minor versions below the newest version the metadata advertises. That is a deliberate-looking choice for a tool people install onto machines they do not control, and it is consistent with the highlight that uv is installable without Rust or Python via curl or pip.

The licensing is worth reading at the same time, because it is dual. The license field is MIT OR Apache-2.0 with two license files, LICENSE-APACHE and LICENSE-MIT, while the repository listing shows Apache-2.0. For a reader choosing terms, the choice is yours to make, and the two files are both in the tree.

One more runtime detail from the feature examples: uv run --python pypy@3.8 runs PyPy 7.3.11, so the version manager covers PyPy as well as CPython, which the classifier list does not mention.

## The repository is itself a uv project, and the Python package wraps a Rust binary

The top level of the tree contains pyproject.toml, uv.lock, and .python-versions, which means the tool is developed with itself. The package metadata then shows how the Python distribution is built: the build backend is maturin, the tool.maturin section sets bindings to bin, manifest-path to crates/uv/Cargo.toml, module-name to uv, and python-source to python, with strip enabled.

Read together, that says the PyPI artifact is a thin wrapper around a compiled Rust binary rather than a Python library. The requires-python floor of 3.8 therefore constrains the interpreter used to install the wrapper, not the machine that runs the work, and the actual logic lives under crates/.

The rest of the top level explains the scale. Alongside the Rust workspace there is uv.schema.json for editor completion of uv's own settings, a ruff.toml, a clippy.toml, an _typos.toml, a dist-workspace.toml, a hawk.toml, an AGENTS.md, agents/, .codex/, .cargo/, .config/, and a test/ directory. A repository that mixes a Rust workspace, Python packaging, JSON schema, two linters, a spell checker, and agent instruction files is not a project with one toolchain, and that shapes what a contribution costs.

## The pip interface is called a drop-in replacement and an extension in the same section

Two sentences sit close together and pull in opposite directions. uv provides a drop-in replacement for common pip, pip-tools, and virtualenv commands. Then, in the next breath, uv extends their interfaces with advanced features, such as dependency version overrides, platform-independent resolutions, reproducible resolutions, alternative resolution strategies, and more.

A drop-in replacement changes nothing about behaviour. An extension changes behaviour. A team that rewrites pip to uv pip in a CI script gets whichever of those two the project meant, and the README presents both descriptions at once.

The concrete artefact to look at is the requirements file. The compile example uses a flag to write a platform-independent file:

```
uv pip compile requirements.in \
   --universal \
   --output-file requirements.txt
```

That is not the same output as a default platform-specific compile, and the same section pairs it with uv pip sync to install the locked result. So a migration is not only a search-and-replace on the command; it regenerates the file your builds install from. Check the diff on that file before the change reaches production, and note that alternative resolution strategies means a resolution can now succeed where pip's would have failed.

## The 10-100x range points at a benchmark file and a warm cache caption

The highlights say 10-100x faster than pip, and the link on that phrase goes to BENCHMARKS.md, which is a file in the repository root rather than a measurement on the page. The image caption above the highlights reads Installing Trio's dependencies with a warm cache.

So the headline is a range, the illustration is a warm cache, and the page supplies no hardware, no Python version, and no package set. A reader who repeats the number in a ticket is repeating a figure whose conditions they have not seen.

The good news is that the conditions are available, in a file the project owns and keeps in the repository. Read it before citing the range.

The same paragraph carries the larger claim, that uv is a single tool to replace pip, pip-tools, pipx, poetry, pyenv, twine, virtualenv, and more. That is the sentence with the real consequences. Adopting it is not a faster pip, it is replacing seven tools at once, each of which somebody on the team configured, and each of which has habits attached to it. The features section even says the projects model is similar to rye or poetry, and that uv can build and publish projects it does not manage, which is a generous migration path and also a sign of how much surface you are taking on.

## Conclusion

uv suits a team willing to standardise on one tool and let it own the environment, the lockfile, and the script metadata, and it is a strong fit for projects that already keep a pyproject.toml. It does not suit a shop that wants pip's exact behaviour with a faster front end, because the pip interface is described as both a drop-in replacement and an extension of it. Before adopting it, agree on which install route each machine uses, since self update only covers the standalone installer, and read BENCHMARKS.md before repeating the speed claim as though it were a property of your project.

## FAQ

### what is astral sh uv

uv is an extremely fast Python package and project manager written in Rust, with its documentation at docs.astral.sh/uv. It is described as a single tool to replace pip, pip-tools, pipx, poetry, pyenv, twine, and virtualenv, and it is backed by Astral, the creators of Ruff and ty.

### Is Astral SH safe?

The README describes installing it by piping a script into your shell, with curl -LsSf https://astral.sh/uv/install.sh | sh on macOS and Linux, or from PyPI with pip install uv or pipx install uv. The project is dual licensed as MIT OR Apache-2.0, supports macOS, Linux, and Windows, and publishes its own security policy.

### install astral sh uv

On macOS and Linux use curl -LsSf https://astral.sh/uv/install.sh | sh. On Windows use powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex". Or install from PyPI with pip install uv, or pipx install uv. Only the standalone installer can then run uv self update.

### what is ghcr io astral sh uv

That is the container image the project publishes, and the project's own Dockerfile copies the uv binary out of it to bootstrap the build. The README's documented install paths are the standalone shell and PowerShell installers or PyPI, and the installation documentation is said to cover alternative methods.

### astral sh uv vscode

The repository ships uv.schema.json at the top level, a JSON schema for uv's own configuration, along with AGENTS.md, agents/, .codex/, and .config/. The README does not describe a Visual Studio Code extension for the project.

## Sources

- [Official documentation](https://docs.astral.sh/uv)
- [Official README](https://github.com/astral-sh/uv#readme)
- [Project repository](https://github.com/astral-sh/uv)
- [Release notes](https://github.com/astral-sh/uv/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/astral-sh-uv
