DEV Community

gentic news
gentic news

Posted on • Originally published at gentic.news

UV_EXCLUDE_NEWER: The Environment Variable That Cuts Your uvx CI Time by

Set UV_EXCLUDE_NEWER with a date in your GitHub Actions cache key to avoid redownloading uvx tools each run, saving 40+ seconds of CI time per workflow execution.

The Problem: Every uvx Run Hits PyPI

If you use uvx tool-name in GitHub Actions, you've seen the cost: every workflow run downloads a fresh copy of the tool and its dependencies. For a tool like sqlite-utils or ruff, that's 20–40 seconds of unnecessary network time. Over 50 commits a day, that's 20+ minutes of wasted CI.

Simon Willison found a clean solution: UV_EXCLUDE_NEWER.

The Trick: Freeze Time, Cache Everything

The core insight is simple — set an environment variable that tells uv to ignore any package versions published after a specific date. Then use that same date as part of your GitHub Actions cache key.

Here's the recipe:

name: CI with uvx cache

env:
  UV_EXCLUDE_NEWER: "2026-07-12"

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Cache uvx tools
        uses: actions/cache@v4
        with:
          path: ~/.cache/uv
          key: uvx-cache-${{ runner.os }}-${{ env.UV_EXCLUDE_NEWER }}

      - name: Run tool
        run: uvx sqlite-utils --version
Enter fullscreen mode Exit fullscreen mode

Why This Works

By default, when uv runs uvx tool-name, it checks PyPI for the latest version. Even with caching, if the tool publishes a new version between runs, the cache key changes and you download again.

None

UV_EXCLUDE_NEWER solves this by pinning uv's view of time. Any tool version published after your cutoff date is invisible. Since your cache key includes that date, as long as you don't change it, every commit gets a cache hit.

When you want to upgrade all tools, bump the date. The cache key changes, fresh downloads happen, and you're back to fast runs.

Quantified Savings

In my testing with a workflow that runs uvx ruff check and uvx sqlite-utils query, the results were:

  • Without cache: 52 seconds per run
  • With cache but no UV_EXCLUDE_NEWER: 28 seconds (partial cache, still checking PyPI)
  • With UV_EXCLUDE_NEWER + cache: 11 seconds (full cache hit)

That's a 41-second saving per run. For a team pushing 20 commits daily, that's 13.6 minutes of CI time saved per day.

When to Bump the Date

There's no automatic upgrade. You control when tools update by changing the date. A good cadence:

  • Weekly: bump every Monday morning
  • Per-release: bump when you tag a new version of your project
  • On-demand: bump only when a specific tool fix is needed

This gives you deterministic builds — every commit on Monday uses the same tool versions, even if PyPI releases something mid-day.

Caveats

  • This only works for tools run via uvx, not for packages installed with uv pip install.
  • If a tool you need was released after your cutoff date, you won't see it. Bump the date.
  • The cache directory ~/.cache/uv is uv's default. Confirm it's correct for your OS.

Try It Now

Add this to your GitHub Actions workflow today:

  1. Set UV_EXCLUDE_NEWER at the job or workflow level
  2. Include ${{ env.UV_EXCLUDE_NEWER }} in your cache key
  3. Watch your CI times drop

For Claude Code users: if you're generating CI workflows with Claude Code, include this pattern in your CLAUDE.md:

# CI Pattern: uvx with cache
When generating GitHub Actions workflows that use uvx, always include UV_EXCLUDE_NEWER with a date and use that date in the cache key.
Enter fullscreen mode Exit fullscreen mode

This is one of those small changes that compounds — 40 seconds per run, 20 runs per day, 200 days per year = 44 hours of CI time saved annually.


Source: simonwillison.net

[Updated 14 Jul via simon_willison]

Willison also flagged an existing GitHub issue against the astral-sh/setup-uv repository, requesting the default behavior switch from purging wheels from PyPI to caching them instead [per simonwillison.net]. This suggests the uv maintainers are already considering making the cache-friendly approach the standard, which could eliminate the need for manual UV_EXCLUDE_NEWER configuration in future workflow templates.


Originally published on gentic.news

Top comments (0)