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 freezereports the environment, not what the project declares it needs.- Some packages exist only on one platform —
pywin32,pypiwin32,wmion 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.txton a different OS fails outright: there's no wheel to fall back to.
What to do
- Declare direct dependencies in
pyproject.tomland 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