Q: What stops a venv from being relocatable?

After re-reading Brett’s excellent blog about how virtual environments work, I’ve been wondering something.

If a venv is just:

  • 3 directories (bin, include, lib/pythonX.Y/site-packages)
  • Optional lib64 symlink

Then is the only reason that venv aren’t relocatable due to the shebangs with absolute urls in the “bin” folder? These shebangs help to “autoactivate” the Python interpreter to pickup site-packages?

If my understanding is correct, and I don’t need any console scripts in “bin”, does that mean that a venv is “relocatable” if I add the parent of site-packages to PYTHONPATH?

Anything else that won’t work as expected?


More or less. As long as there’s nothing in the venv that relies on an absolute path, it should be relocatable. The only thing that’s explicitly absolute is the script wrappers[1].

But we don’t guarantee relocatability (apart from anything else, we have no control over what installed code might do), so it’s very much an “at your own risk” situation.

  1. and that’s deliberate, so they can be copied/symlinked and still work - so don’t ask “why not make script wrappers use relative paths?”, it’s been asked plenty of times before :wink: ↩︎


It won’t operate as a venv in this case, it’s just a search path (which is often fine, certainly if you’re just trying to distribute an app - you might also want to look up ._pth files for this). So sys.base_* attributes will also be set differently, and sysconfig.get_path will pull from a different set.

However, if you bring the python3 symlink and it continues to refer to a real Python executable, it’ll still work as a venv if you launch that. You shouldn’t need to set PYTHONPATH in that case, because it’ll find the pyvenv.cfg and set things up “normally”.


Thanks for the replies!

Makes sense. Im sure there are strange edge-cases, but are there any major areas where this would be important at runtime from an application perspective? Not from installation time (installer or build-backend) perspective?

So after looking at the contents of my own venv. Im beginning to realise they are more useful as “installation time” concerns? Rather than runtime concerns? Yes, they are necessary to keep the system Python clean and to prevent different projects from installing incompatible versions of things, but if you already have all packages resolved and installed into a folder somewhere (aka no build-on-install behaviour), then… you more or less don’t need a venv? You really just need a site-packages and a runtime?

It also depends on your definition of “relocatable”. For some it’s restricted to the same machine, for others it’s the same OS, and others still to any machine.


Thanks. Yes, my context would be a build system or CI system. So ignore for a moment that docker containers exist.

Lets assume that we are building for a fixed target platform represented by the cpu, vendor, os, abi and a fixed target cpython interpreter version (major.minor.patch).

Lets also assume that I am willing to ignore the contents of venv/bin and don’t need them on PATH.

I’m seeing the following folders:
./bin - I am confident that I don’t need activation scripts or any of the console scripts
./etc - These seem to come from jupyterlab, but not by much else. I think I can conceivably configure jupyter not to do this and place jupyter home elsewhere
./include - I have a greenlet.h file here. It is shipped as a header with greenlet, probably so that other extensions modules can build against it. I don’t think I need this at runtime (hopefully)
./lib/site-packages - Yes, this is the main runtime libraries I expect to be on search path
./share/* - Similar to jupyter. This looks like a place where docs and icons and man pages go, that I can ignore for runtime or configure tools that use these to look elsewhere

If you want to make scripts relocatable, I do that for the Hatch distributions that binaries use:

Feel free to copy.

edit: it’s important to note that for the Windows script it required me adding relative path support and therefore you must use the trampoline binaries after this commit Allow relative Python executable paths in Windows trampoline by ofek · Pull Request #3717 · astral-sh/uv · GitHub


venv is a development tool, intended to help developers manage their various projects. You can tell this because it requires a global Python install to work, which is something that typically (averaged across all platforms) only developers will have.

It gets occasionally abused as an installation tool, for those platforms where “people who install things” and “developers” tend to be very overlapping. “Non-developers” don’t want to have to install a developer tool globally in order to just run an app.

From a runtime point of view, anything on sys.path is importable. A venv is one way to indirectly configure sys.path, but there are a multitude of other ways, any of which are perfectly valid from the perspective of “which module will I be importing at runtime”.

1 Like

And one way is to accomplish that is to tell e.g. pip to install into a specific directory and making sure that path is on sys.path (e.g. PYTHONPATH or installing into .).