Skip to content
Latchkey LogoLatchkey home

astral-sh/setup-uv: no @v10 tag, one cache key, and Python selection

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.

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.

When enable-cache auto turns on, the setup-uv cache key segments, and the three ways the post step ends
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.

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.

.github/workflows/ci.yml
  - 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.

InputDefaultWhat it does
versionemptyuv version, latest or latest-known; empty reads required-version, then latest
version-fileemptyuv.toml, pyproject.toml, .tool-versions, requirements.txt or uv.lock
resolution-strategyhighestlowest picks the oldest uv matching a range
python-versionemptySets UV_PYTHON for the rest of the job
python-archemptySets UV_PYTHON_ARCH
activate-environmentfalseCreates a venv and puts it on PATH
venv-path, no-project.venv, falseWhere the venv goes; pass --no-project to uv venv
working-directoryworkspaceWhere config files are read and the venv is created
checksumemptyExpected checksum of the uv download
github-tokengithub.tokenUsed only for GitHub Releases downloads
enable-cacheautoUpload the uv cache to the Actions cache
cache-dependency-globrequirements, constraints, pyproject.toml, uv.lock, *.py.lockFiles hashed into the key
restore-cache, save-cachetrue, autoTurn either half off; auto skips saving on merge_group
cache-suffixemptyAppended to the key
cache-local-pathtemp dirSets UV_CACHE_DIR unless it is already set
prune-cachefalseRun uv cache prune --ci before saving
cache-pythonfalseAlso cache managed Python installs
ignore-nothing-to-cache, ignore-empty-workdirfalseSilence the empty-cache error and the empty-directory warning
tool-dir, tool-bin-diremptySet UV_TOOL_DIR and UV_TOOL_BIN_DIR
manifest-file, download-from-astral-mirrorempty, trueWhere versions and archives come from
add-problem-matchers, quiettrue, falseAnnotations, and info-level log output

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.

.github/workflows/ci.yml
  - 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

ReleaseChange
v6.0.0activate-environment replaces automatic venv activation; working-directory added; pyproject-file and uv-file removed
v7.0.0Runs on node24; respects an existing UV_CACHE_DIR; server-url removed
v8.0.0Immutable releases; no more major or minor tags; old manifest format removed
v9.0.0prune-cache default changed to false
v10.0.0auto cache off for sensitive events; latest-known; Python read from .tool-versions

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: pipsetup-uv enable-cache
Python comes fromThe runner's tool cache or a downloadThe system, setup-python, or uv python install
What is cachedpip's download cacheThe uv cache, optionally managed Pythons
Default key input**/requirements.txtrequirements, constraints, pyproject.toml, uv.lock
Fallback on a key missRestore key without the hashNone
Default on non-GitHub runnersOn when cache is setOff with auto

Key takeaways

  • There is no @v8, @v9 or @v10 tag: pin the commit SHA and pin uv with version.
  • enable-cache: auto is off on any runner GitHub does not host and on four sensitive event types.
  • The cache has no restore keys, so a lockfile change means a cold cache.
  • python-version sets UV_PYTHON; activation is separate and usually unnecessary.

Frequently asked questions

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.

References

A warm uv cache, keyed on uv.lock. Latchkey cache-action. Start free → 30-day trial · No credit card