← Back to Notes

Python Virtual Environments (venv): What They Are, Why You Need Them, and How to Use Them

Every Python project needs its own packages, and installing them system-wide causes conflicts or is outright blocked on modern Linux. This guide explains what a virtual environment is, why it solves that problem, and walks through creating, activating, leaving and resuming one, with a daily workflow and troubleshooting.

N
NOTE

Summary. A virtual environment (venv) is a self-contained folder inside your project that holds its own copy of pip and its own installed packages. Each project gets one, so projects can't break each other, and your system's Python stays untouched.

The whole workflow, in one line: create it once with python3 -m venv .venv, turn it on with source .venv/bin/activate every time you work on the project, install packages with pip, record them in requirements.txt, and turn it off with deactivate.

N
NOTE

Throughout this guide, the example project is a small tool called weather_tracker that lives in ~/projects/weather_tracker and uses two packages: requests (to download data) and rich (to print nice tables). Substitute your own project's name and packages wherever you see them.


Environment

ComponentValue
OSUbuntu 24.04 LTS (also works on Debian, WSL2 and Raspberry Pi OS)
Python3.12 (any version from 3.3 onward includes venv)
Shellbash (zsh works identically)
WindowsCovered separately in Windows (PowerShell)

What a virtual environment is

A virtual environment is just a folder. Inside it are:

  • a link to a Python interpreter
  • its own pip
  • its own site-packages folder, where installed packages go
  • a few small scripts that switch your terminal over to use all of the above
weather_tracker/
+-- .venv/                          < the virtual environment
|   +-- bin/
|   |   +-- activate                < script that turns the venv on
|   |   +-- python -> /usr/bin/python3.12
|   |   +-- pip
|   +-- lib/
|   |   +-- python3.12/
|   |       +-- site-packages/      < this project's installed packages
|   +-- pyvenv.cfg                  < records which Python it was made from
+-- main.py
+-- requirements.txt

When the venv is active, running python or pip uses the copies in .venv/bin/, and any package you install lands in this project's site-packages, not the system's.

N
NOTE

A venv is not a virtual machine or a container. It doesn't emulate hardware, run a separate operating system, or isolate files and networking the way a VM or Docker does. It uses the same Python interpreter already on your system. The only thing it isolates is the set of installed Python packages. That's why it's tiny and instant to create.


Why you need one

1. Projects need different versions of the same package

Imagine two projects on the same machine:

Without virtual environments (one shared site-packages)

weather_tracker --needs--> requests 2.32 -+
                                          +--> system site-packages: only ONE version can exist x
old_script      --needs--> requests 2.25 -+

Installing one version replaces the other, and one of the projects breaks.

With virtual environments (one site-packages per project)

weather_tracker/.venv --> requests 2.32 y
old_script/.venv      --> requests 2.25 y

2. It protects your system's Python

Linux uses Python for its own tools (package managers, system utilities). If you install or upgrade packages system-wide, you can break software your OS depends on. Modern Ubuntu, Debian and others now refuse system-wide pip installs entirely:

$ pip install requests
error: externally-managed-environment

× This environment is externally managed

This isn't a bug. It's the system telling you to use a virtual environment.

3. It makes your project reproducible

Because the venv only contains what this project needs, you can write that list down in a requirements.txt file. Anyone (including you on another machine) can recreate the exact same setup with one command.

4. It's disposable

If a venv gets into a weird state, you delete the .venv folder and recreate it in seconds. Nothing outside your project is affected.


How activation works

"Activating" a venv doesn't install or copy anything. It runs a small script that puts .venv/bin at the front of your shell's PATH, the list of folders the shell searches when you type a command.

Before activating
$ which python3
/usr/bin/python3                                   ← system Python

After activating
$ which python
/home/you/projects/weather_tracker/.venv/bin/python ← the venv's Python

The script also:

  • sets the VIRTUAL_ENV variable to the venv's path
  • adds (.venv) to the start of your prompt, so you can see it's on
  • creates the deactivate command, which undoes all of the above
T
TIP

Activation only affects the terminal you ran it in. Other open terminals, and new ones you open later, start without it. That's why you activate again each time you sit down to work.


Prerequisites

  • Python 3 installed (python3 --version)
  • A terminal (bash or zsh on Linux/macOS/WSL, or PowerShell on Windows)
  • A project folder to work in

Step 1: Install the venv module

The venv module is part of Python itself, but Debian and Ubuntu ship it as a separate package. Install it once:

sudo apt update
sudo apt install python3-venv

Why: Debian-based distributions split Python into smaller packages. Without python3-venv, creating an environment fails with an ensurepip is not available error.

N
NOTE

macOS, Fedora, Arch and Windows include venv with Python already. You can skip this step there.


Step 2: Create the virtual environment

Go to your project folder and create the venv:

mkdir -p ~/projects/weather_tracker
cd ~/projects/weather_tracker
python3 -m venv .venv

Check what was created:

ls -a

Expected:

.  ..  .venv

Why each part:

  • python3 -m venv runs the venv module that comes with Python. -m means "run this module as a program."
  • .venv is the name of the folder to create. You can call it anything, but .venv is the common convention:
    • The leading dot hides it from normal ls output, so it doesn't clutter your project.
    • Editors like VS Code and tools like uv look for .venv automatically.
  • You only do this once per project. The folder stays until you delete it.
T
TIP

Need a specific Python version? Call that version directly: python3.12 -m venv .venv. The venv is permanently tied to whichever Python created it.


Step 3: Activate it

source .venv/bin/activate

Your prompt now starts with (.venv):

(.venv) you@machine:~/projects/weather_tracker$

Verify that python points into the venv:

which python
python --version

Expected:

/home/you/projects/weather_tracker/.venv/bin/python
Python 3.12.3

Why source: a normal script runs in a separate process, so any changes it makes to PATH disappear when it finishes. source runs the script inside your current shell, so its changes stick.

N
NOTE

python vs python3: on Ubuntu, only python3 exists system-wide. Inside an active venv, both python and python3 work and point to the same place. If python gives "command not found," the venv probably isn't active.


Step 4: Install packages

With the venv active:

python -m pip install --upgrade pip
pip install requests rich
pip list

Expected (versions will differ):

Package            Version
------------------ ---------
certifi            2026.x.x
charset-normalizer 3.x.x
idna               3.x
markdown-it-py     3.x.x
pip                26.x
requests           2.32.x
rich               14.x.x
...

Why:

  • Upgrading pip first avoids install errors caused by an outdated pip bundled with the system.
  • pip list shows more than the two packages you asked for. Those extras are dependencies: packages that requests and rich need to work. pip installs them automatically.
  • Everything lands in .venv/lib/python3.12/site-packages/. Your system Python never sees any of it.
T
TIP

python -m pip vs pip: inside an active venv they do the same thing. python -m pip is the safer habit because it guarantees pip installs into whichever Python you're actually running, even when your PATH is confusing.


Step 5: Record your dependencies

Create a requirements.txt file so the setup can be recreated later. There are two common approaches:

Option A: write it by hand, listing only the packages you chose:

requests
rich

Option B: generate it with pip freeze, which records everything installed, with exact versions:

pip freeze > requirements.txt
cat requirements.txt
certifi==2026.x.x
charset-normalizer==3.x.x
idna==3.x
requests==2.32.x
rich==14.x.x
...
Hand-writtenpip freeze
ContentsOnly your direct dependenciesEvery package, with exact versions
ReadabilityEasy to read and editLong, includes dependencies of dependencies
ReproducibilityGets the latest versions at install timeRecreates the exact same versions
Best forSmall tools and learning projectsApps you deploy, where surprises are costly

To recreate the environment from the file, on any machine:

pip install -r requirements.txt

Why: the .venv folder itself never gets shared (see the next step). requirements.txt is the small, portable recipe for rebuilding it.


Step 6: Keep the venv out of Git

Create a .gitignore file in the project root containing:

.venv/
__pycache__/

Why:

  • .venv holds thousands of files that are specific to your machine (including absolute paths). Committing it bloats the repository and won't work on anyone else's computer anyway.
  • __pycache__/ holds compiled files Python generates automatically when it runs your code.
  • Commit requirements.txt instead. It's the recipe, not the meal.

Step 7: Leave the virtual environment

When you're done working:

deactivate

The (.venv) disappears from your prompt, and python and pip go back to the system versions.

Why: deactivate restores the PATH and prompt exactly as they were before you activated. Closing the terminal window has the same effect, since activation only ever lived in that one shell session.

N
NOTE

Deactivating doesn't delete anything. The .venv folder and every package in it stay exactly where they are, ready for next time.


Resuming work later

Each time you come back to the project, in a new terminal:

cd ~/projects/weather_tracker
source .venv/bin/activate

That's all. Your packages are still installed from last time.

If the project uses Git, a good full routine is:

cd ~/projects/weather_tracker
git pull                              # get any changes
source .venv/bin/activate             # turn on the venv
pip install -r requirements.txt       # install anything new in requirements.txt

Why reinstall from requirements.txt: if you (or someone else) added a package on another machine, git pull brings in the updated requirements.txt, but not the package itself. Running the install again adds only what's missing; packages already installed are skipped.

W
WARN

The most common mistake is forgetting to activate. The symptoms are ModuleNotFoundError: No module named '...' for a package you know you installed, or error: externally-managed-environment when installing. Check your prompt for (.venv) first.


Using a venv without activating it

Activation is a convenience for interactive work. Scripts, scheduled jobs (cron, systemd timers) and launcher scripts can skip it and call the venv's Python directly by its path:

~/projects/weather_tracker/.venv/bin/python ~/projects/weather_tracker/main.py

Why this works: the Python inside .venv/bin/ already knows which site-packages belongs to it. Activation only changes which python your shell finds first. Calling the full path gets the same result without changing anything.

The same applies to pip:

~/projects/weather_tracker/.venv/bin/pip install -r requirements.txt

Moving, renaming or rebuilding a venv

W
WARN

Virtual environments can't be moved. A venv stores absolute paths inside its own files: the activate script and the first line of tools like pip literally contain /home/you/projects/weather_tracker/.venv. If you rename or move the project folder, those paths point nowhere, and the venv breaks in confusing ways.

The fix is always the same: delete it and rebuild it from requirements.txt.

deactivate 2>/dev/null          # turn it off if it's on (ignore errors if not)
rm -rf .venv
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Why this is fine: the venv was never the valuable part. Your code and requirements.txt are. Rebuilding takes seconds.

This same routine applies when:

  • you clone a project onto a new machine
  • you upgrade your system's Python to a new major version
  • the venv seems corrupted or behaves strangely
T
TIP

Shortcut: python3 -m venv --clear .venv empties and recreates the venv in place, in one command.


Editor integration (VS Code)

VS Code can use your venv automatically:

  1. Open the project folder (code . from the project root).
  2. Press Ctrl+Shift+P and run Python: Select Interpreter.
  3. Choose the entry that shows .venv.

Why: this tells VS Code's Python tools which packages exist, so it stops underlining your imports as missing. New terminals opened inside VS Code will also activate the venv for you. VS Code remembers the choice per project.

If .venv isn't listed, choose Enter interpreter path and type .venv/bin/python.


Windows (PowerShell)

The ideas are identical. Only the commands and folder names differ.

cd C:\projects\weather_tracker
py -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
pip install -r requirements.txt
deactivate

Differences from Linux:

  • py is the Windows Python launcher. python works too if it's on your PATH.
  • The scripts folder is Scripts\, not bin/.
  • There's no source. You run the activation script directly.
!
BUG

"running scripts is disabled on this system" — PowerShell blocks scripts by default. Allow scripts you create locally (and signed scripts from the internet) for your user only:

Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
ShellActivate command
bash / zshsource .venv/bin/activate
fishsource .venv/bin/activate.fish
PowerShell.venv\Scripts\Activate.ps1
Command Prompt.venv\Scripts\activate.bat

Useful commands

All of these assume the venv is active.

CommandWhat it does
pip listLists every installed package
pip list --outdatedShows packages with newer versions available
pip show requestsDetails for one package: version, location, what it depends on
pip install requestsInstalls a package
pip install requests==2.32.3Installs an exact version
pip install --upgrade requestsUpgrades a package
pip uninstall requestsRemoves a package (its dependencies stay)
pip install -r requirements.txtInstalls everything listed in the file
pip freeze > requirements.txtWrites all installed packages and versions to the file
pip checkReports packages with missing or conflicting dependencies
echo $VIRTUAL_ENVShows which venv is active (empty if none)
which pythonShows which Python your shell will run
deactivateLeaves the venv

A typical daily workflow

# Start of a session
cd ~/projects/weather_tracker
source .venv/bin/activate

# Work
python main.py
pip install some-new-package       # need something new?
pip freeze > requirements.txt      # or add it to requirements.txt by hand
git add requirements.txt
git commit -m "Add some-new-package"

# End of a session
deactivate

Alternatives worth knowing

venv is built into Python and is all most projects need. Two related tools are worth knowing about once you're comfortable:

ToolWhat it's for
pipxInstalling Python command-line tools you want available everywhere (not libraries for a project). Each tool gets its own hidden venv automatically: pipx install <tool>.
uvA much faster replacement for venv + pip. The concepts in this guide carry over directly: uv venv creates .venv, and uv pip install -r requirements.txt installs into it.

Troubleshooting

Work through these in order. The first one that matches is usually the cause.

#CheckCommandIf it's wrong...
1Is the venv active?Look for (.venv) in the promptsource .venv/bin/activate
2Which Python am I running?which pythonShould point into .venv/bin/. If not, activate again
3Is the package installed here?pip show <package>pip install <package> while active
4Am I in the right folder?pwdcd into the project before activating
5Is the venv broken?ls .venv/bin/Rebuild it (see above)
!
BUG

The virtual environment was not created successfully because ensurepip is not available — the python3-venv package is missing. Install it with sudo apt install python3-venv, then delete the half-created .venv folder and run python3 -m venv .venv again.

!
BUG

error: externally-managed-environment when running pip install — the venv isn't active, so pip is trying to install into the system Python. Activate first. Don't work around it with --break-system-packages; that flag exists for special cases, and it's exactly what the protection is there to prevent.

!
BUG

ModuleNotFoundError for a package you installed — either the venv isn't active, or the package went into a different environment. Check:

which python
pip show <package>

If pip show finds nothing, install it again with the venv active.

!
BUG

bad interpreter: No such file or directory, or activate seems to do nothing — the project folder was moved or renamed after the venv was created. The venv's internal paths are now wrong. Delete .venv and rebuild it from requirements.txt.

!
BUG

python: command not found — on Ubuntu, python only exists inside an active venv. Outside one, use python3. If you see this while the prompt shows (.venv), the venv is broken; rebuild it.

!
BUG

VS Code underlines imports as missing, but the code runs fine in the terminal — VS Code is using a different interpreter. Run Python: Select Interpreter and choose the one in .venv.

!
BUG

pip freeze lists packages you never installed — those are dependencies of the packages you did install. That's normal. If you only want your direct dependencies listed, write requirements.txt by hand instead.


Quick reference

# --- One-time setup (per project) ---
sudo apt install python3-venv        # Debian/Ubuntu only, once per machine
cd ~/projects/weather_tracker
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
pip install requests rich
pip freeze > requirements.txt
printf ".venv/\n__pycache__/\n" >> .gitignore

# --- Every session ---
cd ~/projects/weather_tracker
source .venv/bin/activate
# ...work...
deactivate

# --- Rebuild (moved folder, new machine, broken venv) ---
rm -rf .venv
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

# --- Run without activating ---
.venv/bin/python main.py

Key concepts

  • Virtual environment (venv): a folder holding a project's own pip and installed packages, isolated from the system and from other projects.
  • site-packages: the folder where installed packages live. Each venv has its own.
  • Activation: running source .venv/bin/activate, which puts the venv's bin/ folder first in PATH for the current terminal only.
  • PATH: the ordered list of folders your shell searches when you type a command. Whichever folder comes first wins.
  • requirements.txt: the portable list of packages needed to rebuild the environment. Commit this, never the .venv folder.
  • Externally managed environment: the protection on modern Linux that blocks system-wide pip installs, so OS tools that depend on Python can't be broken.
  • Not a VM: a venv isolates Python packages only. It shares the operating system, the files and the Python interpreter with everything else.