Dev In The Mountain Header
A Developer In The mountains having fun

Virtual Environments with venv

If you've installed Python packages with pip install straight into your system Python, you've probably hit (or will soon hit) one of these problems: two projects need different versions of the same library, an upgrade for one project breaks another, or your operating system refuses to let you install anything at all. Virtual environments solve all of these, and venv is the tool that ships with Python to create them.

What is a Virtual Environment?

A virtual environment is a self-contained directory that holds:

  • A link to a Python interpreter: The version you created it with
  • Its own site-packages folder: Where installed packages live
  • Its own copy of pip: Installs go into this environment only
  • Activation scripts: Put the environment's python and pip first on your PATH

Each project gets its own environment, so packages installed for one project never touch another project or your system Python.

Real-world analogy: A virtual environment is like giving each project its own toolbox. You don't have to worry that someone swapped the wrench in your toolbox for a different size while working on their project.

Why It Matters Today

1. Your OS Won't Let You Install Globally Anymore

Modern Linux distributions (Debian 12+, Ubuntu 23.04+, Fedora and others) and Homebrew Python on macOS mark the system Python as externally managed (PEP 668). Try to pip install into it and you'll see:

$ pip install requests
error: externally-managed-environment

ร— This environment is externally managed
โ•ฐโ”€> To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you are trying to
    install.

The system Python belongs to your operating system, and tools you rely on depend on the exact package versions it ships. The fix isn't --break-system-packages - it's a virtual environment.

2. Projects Have Conflicting Dependencies

project_a/  needs  pandas 1.5  (old code, not yet migrated)
project_b/  needs  pandas 2.2  (new code using new features)

Without isolation, only one version can be installed at a time. With a virtual environment per project, both work side by side.

3. Reproducibility

When your dependencies live in an isolated environment, pip freeze lists exactly what your project needs - not every package you've ever installed. That makes it possible for a teammate, a CI server, or a Docker build to recreate your setup exactly.

4. Safe Experimentation

Want to try a new library or a pre-release version? Create a throwaway environment, experiment, and delete the folder when you're done. Nothing else on your machine is affected.

5. Supply-Chain Hygiene

Installing packages runs code from the internet. Keeping each project's packages in its own environment limits what a bad or compromised package can reach, and makes it easy to audit what a project actually depends on.

Using venv

venv has been part of Python's standard library since Python 3.3 - there's nothing extra to install.

Note for Debian/Ubuntu: the venv module is split into a separate package. If you get an error about ensurepip, run sudo apt install python3-venv.

Creating an Environment

# Navigate to your project
cd my_project

# Create a virtual environment in a folder named .venv
python -m venv .venv

# On some systems
python3 -m venv .venv

Naming it .venv is the common convention - VS Code, PyCharm, and most other tools detect it automatically.

Activating the Environment

# macOS/Linux (bash/zsh)
source .venv/bin/activate

# Windows (Command Prompt)
.venv\Scripts\activate.bat

# Windows (PowerShell)
.venv\Scripts\Activate.ps1

Once activated, your prompt shows the environment name:

(.venv) $ which python
/home/you/my_project/.venv/bin/python

PowerShell tip: if activation is blocked, run Set-ExecutionPolicy -Scope CurrentUser RemoteSigned once.

Installing Packages

With the environment active, pip installs into the environment only:

(.venv) $ pip install requests pandas
(.venv) $ pip list

Saving and Restoring Dependencies

# Save the exact versions you're using
pip freeze > requirements.txt

# Recreate the environment somewhere else
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Deactivating

(.venv) $ deactivate
$

Running Without Activating

Activation is just a convenience. You can call the environment's Python directly, which is handy in scripts, cron jobs, and systemd services:

.venv/bin/python main.py
.venv/bin/pip install requests

Deleting an Environment

A virtual environment is just a folder. If it gets messed up, delete it and recreate it:

rm -rf .venv
python -m venv .venv
pip install -r requirements.txt

Best Practices

1. Never Commit the Environment

Add it to your .gitignore:

.venv/

Commit requirements.txt (or pyproject.toml) instead - that's the recipe, the environment is the cooked meal.

2. One Environment Per Project

Keep the .venv folder inside the project it belongs to. It's easy to find, easy for editors to detect, and easy to delete.

3. Don't Move or Rename It

Environments contain absolute paths. If you move the project folder, recreate the environment rather than trying to fix it.

4. Upgrade pip Inside New Environments

python -m pip install --upgrade pip

5. Point Your Editor at It

In VS Code: Ctrl+Shift+P โ†’ "Python: Select Interpreter" โ†’ choose the .venv interpreter. See the Development Environment guide for more editor setup.

Quick Reference

TaskCommand (macOS/Linux)Command (Windows)
Createpython3 -m venv .venvpython -m venv .venv
Activatesource .venv/bin/activate.venv\Scripts\activate
Install packagepip install namepip install name
Save depspip freeze > requirements.txtpip freeze > requirements.txt
Restore depspip install -r requirements.txtpip install -r requirements.txt
Deactivatedeactivatedeactivate
Removerm -rf .venvrmdir /s .venv

Modern Alternatives

venv + pip is the built-in foundation, and it's worth understanding because every other tool builds on the same idea. But it leaves a few jobs to you: installing different Python versions, locking exact dependency versions (including sub-dependencies), and keeping requirements.txt in sync. Newer tools handle those for you.

uv

docs.astral.sh/uv

An extremely fast all-in-one tool written in Rust by Astral (the makers of Ruff). It replaces venv, pip, pip-tools, pyenv, and pipx in one binary.

uv init my_project      # create a project with pyproject.toml
uv add requests         # add a dependency (creates .venv automatically)
uv run main.py          # run inside the environment - no activation needed
uv python install 3.13  # install a Python version

How it differs: 10-100x faster than pip, manages Python versions itself, writes a cross-platform uv.lock lockfile, and creates/syncs the .venv for you. It also has a pip-compatible mode (uv venv, uv pip install) if you want to keep your existing workflow and just make it faster. This is the current go-to recommendation for new projects.

Poetry

python-poetry.org

A mature dependency manager and packaging tool.

poetry new my_project
poetry add requests
poetry run python main.py

How it differs: Manages dependencies in pyproject.toml with a poetry.lock lockfile and a strong dependency resolver, and builds/publishes packages to PyPI. It creates virtual environments for you but doesn't install Python versions. Slower than uv, but widely used and well documented.

Pipenv

pipenv.pypa.io

One of the first tools to combine pip and virtualenv.

How it differs: Uses a Pipfile and Pipfile.lock instead of requirements.txt. Still maintained and found in plenty of existing projects, but most new projects choose uv or Poetry instead.

Conda / Mamba / Pixi

docs.conda.io ยท pixi.sh

Environment managers from the scientific Python world.

How it differs: Can install non-Python dependencies (C libraries, CUDA, compilers, R) alongside Python packages, which is why they're popular in data science and machine learning. Environments typically live in a central location rather than in your project folder. Mamba is a faster drop-in for conda; Pixi is a newer, project-based tool built on the conda ecosystem.

pyenv

github.com/pyenv/pyenv

How it differs: Not a venv replacement - it installs and switches between multiple Python versions. Traditionally paired with venv (pyenv picks the Python, venv isolates the packages). uv now covers both jobs.

Comparison

ToolCreates envsInstalls Python versionsLockfileNon-Python depsSpeed
venv + pipโœ…โŒโŒโŒBaseline
uvโœ…โœ…โœ…โŒVery fast
Poetryโœ…โŒโœ…โŒModerate
Pipenvโœ…โŒโœ…โŒSlow
Conda / Pixiโœ…โœ…Pixi โœ…โœ…Moderate / fast

Which should you use?

  • Learning Python or writing a quick script? venv - it's always there and teaches you how environments actually work.
  • Starting a new project? uv - fast, simple, and handles everything.
  • Joining an existing project? Use whatever it already uses (look for uv.lock, poetry.lock, Pipfile, or environment.yml).
  • Heavy data science / GPU work with native libraries? Conda or Pixi.

What's Next?

Resources


Make creating a virtual environment the first thing you do in every new Python project. It takes five seconds and saves hours of dependency headaches later.

More places to find me
Mental Health
follow me on Mastodon