2025, Nov 07 21:00

How to install PyPI source-only packages on production: build wheels on a separate box and install offline with pip --no-index

Stuck with a PyPI sdist on production? Use a build machine to create wheels via pip download/wheel, transfer them, then install offline with --no-index.

When a production host can’t compile from source but needs a package that ships only as an sdist on PyPI, the usual pip workflow breaks down. This is common if you segregate concerns: a “build” machine with full toolchain and a “production” machine locked down to the essentials. Even if both boxes share the same architecture, Linux distro, and PyPy installed via pyenv, missing build dependencies on production will block installation.

Problem

You can install on the build box just fine, but the same command on production would require compilers and headers you don’t have or can’t add:

python -m pip install sample-lib

PyPI provides only a source distribution in this case, so pip tries to build from source and fails on the production host.

What’s actually going wrong

The core issue isn’t pip or Python; it’s the lack of build dependencies on the production machine. With no pre-built wheel available on PyPI, pip falls back to compiling the sdist, which is practical on the build box but not on production. You need a way to reuse what the build machine can already produce and avoid compilation entirely on the target.

Solution: pre-build wheels offline and install locally

The workaround is to use the build machine to fetch everything needed and turn any source archives into wheels, then copy the results to production and install from local files. Start by downloading the package and its dependencies on the build box into a dedicated directory:

python -m pip download sample-lib -d ./wheelstash

That command places wheels and source archives side-by-side. If any files arrive as .tar.gz, convert them into wheels so production won’t need to build anything:

python -m pip wheel --no-deps --wheel-dir=./wheelstash ./wheelstash/*.tar.gz

Move the entire wheelstash directory to the production machine using your usual transfer method. Then install from the local path without reaching out to PyPI:

python -m pip install --no-index --find-links=./wheelstash sample-lib

If your packaging workflow requires it, you may also need setuptools and wheel. For reference details on the download command, see: https://pip.pypa.io/en/latest/cli/pip_download/.

Why this matters

This approach lets production stay lean and predictable. You avoid introducing compilers or headers onto a sensitive host, and you sidestep network calls to external registries during deployment. The build machine does the heavy lifting once; production simply unpacks prebuilt wheels.

Conclusion

When PyPI doesn’t offer a wheel, rely on a controlled build pipeline: download on a capable machine, convert any sdists into wheels, and install from a local wheel directory on production. Keep the two environments aligned in architecture and Python runtime, cache the artifacts you produce, and prefer local installs with --no-index and --find-links to make deployments repeatable and safe.

The article is based on a question from StackOverflow by mysteryegg and an answer by Viktor Sbruev.