A virtual environment is a self-contained directory holding a reference to a
base interpreter, a private site-packages directory, and its own executable
scripts. Packages installed into it are visible only to that environment, so
projects can depend on different versions of the same library without conflict.
The venv module ships with the standard library and creates one.
Isolation is not a stylistic preference. Without it, every project shares one package set, a single upgrade can break unrelated tooling, resolution reflects the history of the workstation rather than the project's declaration, and externally managed interpreters refuse installation.
A virtual environment is inexpensive and expendable: build output rather than source. Never commit it, never repair it by hand, and recreate it whenever the base interpreter changes or the installed state becomes uncertain.
Create a virtual environment in the current project. The directory name .venv
is a widely recognized convention understood by most editors and tools:
python3 -m venv .venvThe interpreter used to run this command becomes the base interpreter of the
environment. To target a specific release, invoke it explicitly, for example
python3.13 -m venv .venv.
| Option | Effect |
|---|---|
--prompt <name> |
Sets the name shown in the shell prompt |
--system-site-packages |
Grants access to the base interpreter packages |
--clear |
Deletes existing contents before creating |
--upgrade |
Upgrades the environment to the running interpreter |
--upgrade-deps |
Upgrades pip in the new environment |
Avoid --system-site-packages for application projects: it reintroduces the
base interpreter's packages into the resolution path and defeats isolation.
Since Python 3.12, a new environment contains pip but not setuptools, which
projects requiring it must declare.
Activation prepends the environment's bin directory to PATH and adjusts the
shell prompt. It is a convenience, not a requirement. Activate in zsh or
bash, then leave the environment when work is complete; deactivate is a
shell function defined by the activation script that restores the previous
PATH:
source .venv/bin/activate
deactivateActivation applies only to the current shell session and does not persist to
new terminals, to subshells started before activation, or to scheduled
processes. Do not place an activation command in .zshrc: it forces one
project's environment onto every shell.
Invoking the environment's interpreter by path produces identical behavior and is preferable in scripts, editors, and continuous integration jobs, where shell state is not guaranteed:
.venv/bin/python -m pip install -r requirements.txtThe activation script sets VIRTUAL_ENV to the absolute path of the
environment root, and deactivate unsets it. Many tools read this variable to
detect an active environment, so it is the most direct signal of activation
state. An empty value means no environment is active, and setting the variable
by hand activates nothing: it only misleads tools that trust it. Setting
VIRTUAL_ENV_DISABLE_PROMPT before activation suppresses the prompt change.
Verify the recorded root, the interpreter, the release, and the installer binding together. All must reference the same environment, and the two prefixes below differ only while an environment is active:
echo "$VIRTUAL_ENV"
command -v python
python --version
python -m pip --version
python -c "import sys; print(sys.prefix, sys.base_prefix, sep='\n')"Each environment records its origin in a pyvenv.cfg file at its root:
home = /opt/homebrew/opt/python@3.13/bin
include-system-site-packages = false
version = 3.13.5The home value identifies the base interpreter the environment depends on.
When that path no longer exists, the environment is unusable and must be
recreated, not edited.
A virtual environment binds to its base interpreter by path. A minor release upgrade, a cleanup, or a change of version manager can remove that path and leave the environment broken. Recreation is the supported repair.
Confirm first that every dependency is declared in a requirements file or in
pyproject.toml; where that record is incomplete, capture the current state
with python -m pip freeze before changing anything. Then recreate:
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -r requirements.txtDo not delete an environment until the required dependencies are declared and
the target path has been verified. Prefer python3 -m venv --clear .venv when
recreating in place, because it names the directory explicitly instead of
relying on a separate removal command.
Add the environment directory to the project's .gitignore. Never commit a
virtual environment: it holds absolute paths, platform-specific binaries, and
files derivable from the declaration.
.venv/
Commit the dependency declaration and any lock artifact instead: those files, not the environment, are the reproducible record.
| Dimension | Virtual environment | Container |
|---|---|---|
| Isolation scope | Python packages only | Full userspace filesystem |
| System libraries | Shared with the host | Declared in the image |
| Build toolchain | Must exist on the host | Declared in the image |
| Startup cost | Negligible | Image build and runtime overhead |
| Production parity | Partial | High when the image is deployed |
A virtual environment isolates Python packages but not compilers, system libraries, locales, or operating system releases. A container isolates the entire userspace and is deployed as the same artifact that was tested, which is the stronger reproducibility guarantee.
The two are complementary. Use a virtual environment for local development speed, and a container when parity with the deployment target, non-Python system dependencies, or reproducible builds are required.
- Isolate every project in a virtual environment or container.
- Create the environment from a managed interpreter, never from a system one.
- Keep the environment directory out of version control.
- Reinstall from the declaration rather than repairing by hand, and recreate environments after any interpreter upgrade or cleanup. Compiled extension modules must be reinstalled after a minor release change.
- Prefer explicit interpreter paths over activation in automated contexts.
- Verify
VIRTUAL_ENVandsys.executablebefore installing packages. - Do not grant access to system site packages without a documented reason.
Inspect the resolution order and the recorded environment root. A shell that cached a path before activation may still resolve the previous interpreter, which the last command corrects:
type -a python
echo "$VIRTUAL_ENV"
hash -rThe function exists only in a shell where the activation script was sourced. Running the script in a subshell leaves the parent shell unmodified, so source it again in the current shell.
Inspect the recorded base interpreter and confirm that it still exists. If the
path is missing, recreate the environment; editing pyvenv.cfg is not a repair:
python -c "import sys; print(sys.base_prefix)"Confirm the installer binding with python -m pip --version. The reported path
must be inside the environment directory; if it is not, the environment is not
active and the installation targeted another interpreter.
- Python Interpreters and Installation
- Python Dependency Management
- Python Project Metadata and Packaging
- Docker images and containers
- Python
venvdocumentation: https://docs.python.org/3/library/venv.html - Python Packaging User Guide: https://packaging.python.org/
- pip documentation: https://pip.pypa.io/