Skip to content
← Blog

Technical explainer

uv vs pip vs poetry for security

3 min read

The Python ecosystem now has three mainstream package managers for projects: pip (the default), poetry (the established alternative), and uv (the new Rust-based option from Astral). From a supply chain security perspective, they differ significantly.

pip

Lockfile: requirements.txt (not a true lockfile — requires manual hash generation) Hash verification: Optional, via --require-hashes flag Frozen install: pip install --require-hashes -r requirements.txt

pip is the most flexible and widely used, but has the weakest default security posture. A plain requirements.txt without version pins or hashes provides no protection against a compromised package published at a new version number. Adding hashes requires running pip-compile --generate-hashes from pip-tools.

pip does not natively verify that a downloaded package's hash matches any known-good value unless you explicitly provide hashes in your requirements file.

Security verdict: Usable with proper configuration, but requires extra steps. The defaults are not secure.

poetry

Lockfile: poetry.lock (auto-generated, includes SHA-256 hashes for every package) Hash verification: Built-in — poetry always verifies hashes against poetry.lock Frozen install: poetry install --no-update

poetry was the first major Python package manager to include lockfile-based hash verification by default. When you run poetry install, it checks every downloaded package against the hash in poetry.lock. If there's a mismatch, the install fails.

The poetry.lock file is comprehensive — it includes direct dependencies, all transitive dependencies, and hashes for all platform-specific wheels. It is committed to version control, giving teams a shared source of truth.

Security verdict: Strong default security posture. Hash verification is always on. The main risk is dependency on the poetry lockfile being up-to-date and not manually edited.

uv

Lockfile: uv.lock (auto-generated, includes SHA-256 hashes, faster than poetry) Hash verification: Built-in — uv sync --frozen always verifies hashes Frozen install: uv sync --frozen

uv is the newest option and has the strongest security defaults. The uv.lock format is more verbose and explicit than poetry's — it records platform-specific packages separately, making the dependency resolution fully deterministic and auditable. uv sync --frozen refuses to modify the lockfile and verifies every downloaded package against its locked hash.

uv is also significantly faster than both pip and poetry — install times are 10–100x faster in many benchmarks. This matters for CI because it reduces the window during which CI runners are installing packages.

Frozen install comparison:

| Manager | Default behavior | Frozen install command | Hash verification | |---|---|---|---| | pip | Installs any version satisfying ranges | pip install --require-hashes -r requirements.txt | Only with --require-hashes | | poetry | Installs from lockfile | poetry install --no-update | Always (from lockfile) | | uv | Installs from lockfile | uv sync --frozen | Always (from lockfile) |

What none of them protect against

All three package managers share the same limitation: they verify packages against a reference you established at lockfile generation time. They don't protect you against packages that were malicious when you generated the lockfile, against packages published after the lockfile was generated (if you update), or against behavioral attacks that don't change the hash (legitimate code with malicious logic that was always in the source).

This is where Veln complements all three: Veln adds behavioral analysis, obfuscation scanning, and community consensus checking that are independent of whether you have hashes for a package.

Recommendation

  • New Python projects: Use uv. Fastest, strongest defaults, best security posture.
  • Existing projects on poetry: Stay on poetry. The security properties are equivalent to uv; the migration cost is not worth it for security alone.
  • Existing projects on pip: Migrate to pip-tools and add --require-hashes, or migrate to uv. The default pip configuration is the weakest option.

Regardless of which package manager you use, run Veln on top. The package manager handles hash verification of known-good packages. Veln handles the threat class that hash verification can't catch.


Veln works with uv, pip, and poetry — adding the verification layer all three are missing.