Safeguard
Developer Tools•

How do you reproduce a Python environment with uv exclude-newer?

uv's exclude-newer option limits resolution to files uploaded before a date, so a rebuild ignores later releases. How it works and where it stops.

Safeguard Security Team
3 min read

Give uv an exclude-newer date and it resolves only distributions uploaded to the package index before that moment, treating anything newer as if it does not exist. That lets you rebuild an environment later without picking up releases published since, and it doubles as a simple cooldown against brand new malicious versions.

What the documentation says

The uv resolution documentation describes exclude-newer as a way to limit resolution to distributions uploaded before a specific date, allowing reproduction of installations regardless of new package releases. Details that matter:

  • The date is compared against the upload time of each individual distribution file, not the release date of the version.
  • It can be an RFC 3339 timestamp or a local date in your configured time zone.
  • The index must support the upload-time field defined in a Python packaging standard. PyPI provides upload-time for all packages.
  • It applies to packages read from a registry, not to things like git dependencies.
  • Package specific overrides exist, and an index can be configured with its own value or opted out.

A quirk worth knowing

To keep builds reproducible, error messages for unsatisfiable resolutions will not mention that a distribution was excluded by this option. Newer files are treated as nonexistent. If a resolution fails and you cannot see why, check whether the date is hiding the version you expected.

Two ways to use it

For reproducibility. Pick the date you built the environment, record it in project configuration and rebuild later with the same result.

As a rolling delay. Set the date to a few days ago each time you update. Fresh uploads cannot be resolved until they have aged. This is the same idea as a cooldown in other tools, and it has the same limitation: malware that stays undetected longer than your window still gets in.

How it relates to the lockfile

The uv lockfile, uv.lock, is created with a universal resolution and is portable across platforms, so dependencies are locked for everyone regardless of operating system, architecture and Python version. The documentation also notes that uv prefers versions already listed in a lockfile or requirements output, so locked versions will not change unless you ask.

In short, the lockfile pins what you chose. Exclude-newer limits what you could choose in the first place.

Steps for a laptop

  1. Commit uv.lock and review its diff like source code.
  2. Set a delay date or window when you update dependencies.
  3. Update deliberately, then read what changed.
  4. If an urgent fix is newer than your date, override that one package, and make a note.

What it does not do

It does not verify hashes of what you download or judge whether a package is malicious. Use hash checking where you can and scan what ends up in the environment.

If you want a quick look at what your projects actually resolve, Safeguard offers SBOM and dependency scanning, and there is a free scan at gold.safeguard.sh/scan.

Quick check

Does exclude-newer use the release date or the upload date?

The upload time of each distribution file, according to the uv documentation, not the release date of the package version.

Does it apply to git dependencies?

No. The option is applied only to packages read from a registry.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.