Skip to main content

pip freeze captures the machine, not the project

Summary: pip freeze dumps every package installed in the current environment, including platform-specific ones. A requirements file produced on one OS can therefore be uninstallable on another — which is how a laptop's dependencies break a Linux container build.

Why it happens

  • pip freeze reports the environment, not what the project declares it needs.
  • Some packages exist only on one platform — pywin32, pypiwin32, wmi on Windows; pyobjc-* on macOS. They arrive as transitive dependencies you never asked for.
  • Pinned with no marker they're unconditional, so pip install -r requirements.txt on a different OS fails outright: there's no wheel to fall back to.

What to do

  • Declare direct dependencies in pyproject.toml and let a lockfile tool (uv, poetry, pip-tools) resolve transitives per platform. Freezing an environment is the fragile way.
  • Use environment markers when a dependency really is conditional: pywin32==311; sys_platform == "win32" — pip skips it elsewhere instead of failing.
  • Review the diff on every freeze. It's generated output, not truth.
  • Build the image in CI on every push, so the container answers "does this install", rather than finding out at deploy time.

Where this came up

requirements.txt was frozen on Windows and carried pywin32==311. The image builds FROM python:3.14-slim, where that package has no installable distribution, so the build died on pip install.

 python-dotenv==1.2.2
-pywin32==311
qdrant-client==1.18.0