Running Python tools with uvx in GitHub Actions is great—until each run re-downloads packages. The fix is simple: cache uv’s shared package directory between jobs to avoid repeat installs and shave minutes off CI.
Credit: this tip builds on Simon Willison’s note about caching uvx in Actions—simple and very effective. See his post: uvx GitHub Actions cache.
The 2-line cache for uvx on GitHub Actions
- Add a cache step early in your workflow that targets
~/.cache/uv(where uv stores wheels, sdists, and tool environments):uses: actions/cache@v4with: path: ~/.cache/uv - Key the cache to your OS, Python version, and dependency files so it refreshes only when needed:
key: ${{ runner.os }}-py${{ matrix.python-version }}-uv-${{ hashFiles('**/uv.lock', '**/pyproject.toml') }}restore-keys: ${{ runner.os }}-py${{ matrix.python-version }}-uv-
Minimal workflow example
Place the cache step before any uvx commands, then run your tools directly:
- uses: actions/checkout@v4- uses: actions/setup-python@v5withpython-version: ${{ matrix.python-version }}- uses: astral-sh/setup-uv@v4- uses: actions/cache@v4withpath: ~/.cache/uvkey: ${{ runner.os }}-py${{ matrix.python-version }}-uv-${{ hashFiles('**/uv.lock', '**/pyproject.toml') }}restore-keys: ${{ runner.os }}-py${{ matrix.python-version }}-uv-- run: uvx ruff check .- run: uvx pytest -q
Why this works
uv maintains a shared cache in ~/.cache/uv. uvx reuses that cache for tools and dependencies, so persisting it across CI runs prevents repeated downloads and build steps. On a typical repo, that means faster lint, test, and packaging jobs with zero changes to your Python project.
Tips and gotchas
- Key by Python version: include
${{ matrix.python-version }}in the cache key to avoid cross-version wheel conflicts. - Lock-aware keys: if you use
uvto manage project deps, includinguv.lock(and/orpyproject.toml) inhashFileskeeps caches fresh when dependencies change. - Size limits: GitHub Actions imposes cache size quotas. Prune occasionally with
uv cache pruneor adjust what you cache if it grows too large. - Matrix jobs: the restore-keys prefix lets related jobs fall back to a near match when an exact key isn’t found, improving cache hit rates.
More on how uv manages environments and caching: uv documentation.
Takeaway
Cache ~/.cache/uv and key it to OS, Python, and your lockfile. Your uvx-powered CI becomes both reproducible and fast—without changing your project layout.
Liked this nugget? Get more practical AI/dev tips in your inbox—subscribe to The AI Nuggets newsletter.

