astral-sh/setup-uv installs uv, puts it on PATH and, on GitHub-hosted runners, saves the uv cache under a key built from the platform, the Python version and a hash of your dependency files. Since v8 it publishes no major tags, so astral-sh/setup-uv@v10 does not resolve: pin a full version or a commit SHA.

No restore keys: a changed lockfile is a cold cache. Read from src/utils/inputs.ts, src/cache/restore-cache.ts and src/save-cache.ts at v10.3.3 on 2026-11-12.
Most setup-uv questions are one of three: why @v8, @v9 or @v10 fails to resolve, why the cache never restores on a branch or on a self-hosted runner, and whether you still need actions/setup-python. The answers are in the release notes and in a few lines of src/utils/inputs.ts and src/cache/restore-cache.ts.
The first stops a workflow before any step runs. setup-uv has published only full version tags since v8, so @v10, the form most other actions use, is not a ref GitHub can resolve. v10.3.3 is the current release, and everything below is read from that tag.
Pinning: there is no @v10
v8.0.0 was the first immutable release, and its notes say the project will stop publishing major and minor tags because floating tags expose users to supply chain attacks. @v7 and earlier still exist. Anything later fails before the job starts with: Unable to resolve action astral-sh/setup-uv@v10, unable to find version v10. That message is the title of issue #1091.
Pin the commit SHA with the version in a comment, and let Dependabot or Renovate bump it. Pin uv itself separately with version, as uv's own GitHub Actions guide recommends; otherwise the action looks for required-version in uv.toml or pyproject.toml and falls back to the latest uv.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
with:
version: "0.13.8"
- run: uv sync --locked
- run: uv run pytest
Twenty-seven inputs, four you will touch
Twenty-seven inputs. The ones most workflows touch are version, python-version, enable-cache and cache-dependency-glob.
| Input | Default | What it does |
|---|---|---|
version |
empty | uv version, latest or latest-known; empty reads required-version, then latest |
version-file |
empty |
uv.toml, pyproject.toml, .tool-versions, requirements.txt or uv.lock
|
resolution-strategy |
highest |
lowest picks the oldest uv matching a range |
python-version |
empty | Sets UV_PYTHON for the rest of the job |
python-arch |
empty | Sets UV_PYTHON_ARCH
|
activate-environment |
false |
Creates a venv and puts it on PATH |
venv-path, no-project
|
.venv, false
|
Where the venv goes; pass --no-project to uv venv
|
working-directory |
workspace | Where config files are read and the venv is created |
checksum |
empty | Expected checksum of the uv download |
github-token |
github.token |
Used only for GitHub Releases downloads |
enable-cache |
auto |
Upload the uv cache to the Actions cache |
cache-dependency-glob |
requirements, constraints, pyproject.toml, uv.lock, *.py.lock
|
Files hashed into the key |
restore-cache, save-cache
|
true, auto
|
Turn either half off; auto skips saving on merge_group
|
cache-suffix |
empty | Appended to the key |
cache-local-path |
temp dir | Sets UV_CACHE_DIR unless it is already set |
prune-cache |
false |
Run uv cache prune --ci before saving |
cache-python |
false |
Also cache managed Python installs |
ignore-nothing-to-cache, ignore-empty-workdir
|
false |
Silence the empty-cache error and the empty-directory warning |
tool-dir, tool-bin-dir
|
empty | Set UV_TOOL_DIR and UV_TOOL_BIN_DIR
|
manifest-file, download-from-astral-mirror
|
empty, true
|
Where versions and archives come from |
add-problem-matchers, quiet
|
true, false
|
Annotations, and info-level log output |
From
action.ymlat v10.3.3 (commit 68559e9), read 2026-11-12. Outputs:uv-version,uv-path,uvx-path,cache-hit,cache-key,venv,python-version,python-runtime-id,python-cache-hit.
enable-cache, and the key it saves under
With auto, caching is on only when RUNNER_ENVIRONMENT is github-hosted. GitHub documents the other value as self-hosted, which covers every runner GitHub does not host, so on those auto means off and you set enable-cache: true. At v10.3.3 auto also turns caching off for tag pushes, pull_request_target, workflow_run and release, to keep those events from restoring a poisoned cache; v10.0.0 introduced the event list.
The key is setup-uv-2-, then the architecture, platform, OS name and version, and the Python version, then -pruned and -py when those options are on, then a hash of the files matching cache-dependency-glob, then your suffix. There are no restore keys, so a changed lockfile starts from an empty cache. The cache itself lives where UV_CACHE_DIR points, which the action sets to /tmp/setup-uv-cache on Linux and macOS unless you or your config already chose a path. Since v9 nothing is pruned before the save; prune-cache: true brings back uv cache prune --ci, which drops pre-built wheels and keeps the ones uv built from source. If the glob matches nothing, the action warns that the cache will never get invalidated, and the warning itself tells you to check that the repository was checked out first.
Two jobs with the same dependency files but different resolution strategies compute the same key and race to save it. The README's answer is a cache-suffix per strategy. On an exact hit the post step saves nothing. When caching is on and uv wrote nothing, the post step fails with Cache path <path> does not exist on disk. unless ignore-nothing-to-cache is set.
- uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
with:
enable-cache: true
cache-dependency-glob: uv.lock
cache-suffix: ${{ matrix.resolution }}
python-version and activate-environment
python-version exports UV_PYTHON, which overrides .python-version and the version in pyproject.toml for every later uv command; it is the documented way to drive a matrix. It does not activate anything. Until v6 it also created a venv; v6 moved that to activate-environment, which runs uv venv, adds the venv's bin directory to PATH and sets VIRTUAL_ENV.
The docs warn against activation for most workflows: a dependency that ships its own uv, such as hatch, then shadows the uv the action installed. uv run gives the same environment without touching PATH.
v6 to v10: activation, tags, pruning, auto cache
| Release | Change |
|---|---|
| v6.0.0 |
activate-environment replaces automatic venv activation; working-directory added; pyproject-file and uv-file removed |
| v7.0.0 | Runs on node24; respects an existing UV_CACHE_DIR; server-url removed |
| v8.0.0 | Immutable releases; no more major or minor tags; old manifest format removed |
| v9.0.0 |
prune-cache default changed to false
|
| v10.0.0 |
auto cache off for sensitive events; latest-known; Python read from .tool-versions
|
From the GitHub release notes for each tag, read 2026-11-12.
setup-uv or setup-python plus pip
They are not exclusive. uv's own guide shows both: uv python install after setup-uv, or setup-python before it, and says setup-python can be faster because GitHub keeps several Python versions on the runner image. The setup-uv README puts that at about a second.
The real difference is the cache. setup-python's pip cache stores pip's download cache with a restore key that falls back to an older entry; setup-uv stores the uv cache, including wheels uv built from source, on one key. Caching pip and Poetry measures what the pip cache is worth.
setup-python cache: pip
|
setup-uv enable-cache
|
|
|---|---|---|
| Python comes from | The runner's tool cache or a download | The system, setup-python, or uv python install
|
| What is cached | pip's download cache | The uv cache, optionally managed Pythons |
| Default key input | **/requirements.txt |
requirements, constraints, pyproject.toml, uv.lock
|
| Fallback on a key miss | Restore key without the hash | None |
| Default on non-GitHub runners | On when cache is set |
Off with auto
|
setup-python keys from
src/cache-distributions/pip-cache.tsat v7.1.0; setup-uv fromsrc/cache/restore-cache.tsat v10.3.3. Read 2026-11-12.
FAQ
Why does astral-sh/setup-uv@v10 fail with unable to find version v10?
Because the tag does not exist. Since v8.0.0 the project publishes only full version tags such as v10.3.3, as a supply chain measure. Pin the full version or, better, the commit SHA with the version in a comment.
Do I still need actions/setup-python alongside setup-uv?
No. uv can install Python with uv python install, or use the one on the runner. The setup-uv README notes that setup-python can be faster because the runner image already carries several versions, so either combination is reasonable.
Why do I see No GitHub Actions cache found for key in setup-uv?
Because the key changed, usually after a dependency file changed, and there is no restore key to fall back to. The README calls it expected on the first run of a new key. If it appears on every run, check that checkout runs first and that cache-dependency-glob matches real files.
Does setup-uv install my project or its dependencies automatically?
No. It installs uv and optionally sets the Python version and a venv. Run uv sync --locked, uv pip install, or uv run, which syncs before running, as a separate step.
Top comments (0)