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.
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.
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
| Component | Value |
|---|---|
| OS | Ubuntu 24.04 LTS (also works on Debian, WSL2 and Raspberry Pi OS) |
| Python | 3.12 (any version from 3.3 onward includes venv) |
| Shell | bash (zsh works identically) |
| Windows | Covered 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-packagesfolder, 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.txtWhen 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.
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 y2. 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 managedThis 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 PythonThe script also:
- sets the
VIRTUAL_ENVvariable to the venv's path - adds
(.venv)to the start of your prompt, so you can see it's on - creates the
deactivatecommand, which undoes all of the above
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-venvWhy: Debian-based distributions split Python into smaller packages. Without
python3-venv, creating an environment fails with an ensurepip is not available error.
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 .venvCheck what was created:
ls -aExpected:
. .. .venvWhy each part:
python3 -m venvruns thevenvmodule that comes with Python.-mmeans "run this module as a program.".venvis the name of the folder to create. You can call it anything, but.venvis the common convention:- The leading dot hides it from normal
lsoutput, so it doesn't clutter your project. - Editors like VS Code and tools like
uvlook for.venvautomatically.
- The leading dot hides it from normal
- You only do this once per project. The folder stays until you delete it.
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/activateYour prompt now starts with (.venv):
(.venv) you@machine:~/projects/weather_tracker$Verify that python points into the venv:
which python
python --versionExpected:
/home/you/projects/weather_tracker/.venv/bin/python
Python 3.12.3Why 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.
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 listExpected (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
pipfirst avoids install errors caused by an outdatedpipbundled with the system. pip listshows more than the two packages you asked for. Those extras are dependencies: packages thatrequestsandrichneed to work. pip installs them automatically.- Everything lands in
.venv/lib/python3.12/site-packages/. Your system Python never sees any of it.
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
richOption B: generate it with pip freeze, which records everything
installed, with exact versions:
pip freeze > requirements.txt
cat requirements.txtcertifi==2026.x.x
charset-normalizer==3.x.x
idna==3.x
requests==2.32.x
rich==14.x.x
...| Hand-written | pip freeze | |
|---|---|---|
| Contents | Only your direct dependencies | Every package, with exact versions |
| Readability | Easy to read and edit | Long, includes dependencies of dependencies |
| Reproducibility | Gets the latest versions at install time | Recreates the exact same versions |
| Best for | Small tools and learning projects | Apps you deploy, where surprises are costly |
To recreate the environment from the file, on any machine:
pip install -r requirements.txtWhy: 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:
.venvholds 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.txtinstead. It's the recipe, not the meal.
Step 7: Leave the virtual environment
When you're done working:
deactivateThe (.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.
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/activateThat'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.txtWhy 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.
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.pyWhy 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.txtMoving, renaming or rebuilding a venv
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.txtWhy 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
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:
- Open the project folder (
code .from the project root). - Press
Ctrl+Shift+Pand run Python: Select Interpreter. - 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
deactivateDifferences from Linux:
pyis the Windows Python launcher.pythonworks too if it's on yourPATH.- The scripts folder is
Scripts\, notbin/. - There's no
source. You run the activation script directly.
"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| Shell | Activate command |
|---|---|
| bash / zsh | source .venv/bin/activate |
| fish | source .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.
| Command | What it does |
|---|---|
pip list | Lists every installed package |
pip list --outdated | Shows packages with newer versions available |
pip show requests | Details for one package: version, location, what it depends on |
pip install requests | Installs a package |
pip install requests==2.32.3 | Installs an exact version |
pip install --upgrade requests | Upgrades a package |
pip uninstall requests | Removes a package (its dependencies stay) |
pip install -r requirements.txt | Installs everything listed in the file |
pip freeze > requirements.txt | Writes all installed packages and versions to the file |
pip check | Reports packages with missing or conflicting dependencies |
echo $VIRTUAL_ENV | Shows which venv is active (empty if none) |
which python | Shows which Python your shell will run |
deactivate | Leaves 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
deactivateAlternatives worth knowing
venv is built into Python and is all most projects need. Two related tools are
worth knowing about once you're comfortable:
| Tool | What it's for |
|---|---|
| pipx | Installing Python command-line tools you want available everywhere (not libraries for a project). Each tool gets its own hidden venv automatically: pipx install <tool>. |
| uv | A 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.
| # | Check | Command | If it's wrong... |
|---|---|---|---|
| 1 | Is the venv active? | Look for (.venv) in the prompt | source .venv/bin/activate |
| 2 | Which Python am I running? | which python | Should point into .venv/bin/. If not, activate again |
| 3 | Is the package installed here? | pip show <package> | pip install <package> while active |
| 4 | Am I in the right folder? | pwd | cd into the project before activating |
| 5 | Is the venv broken? | ls .venv/bin/ | Rebuild it (see above) |
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.
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.
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.
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.
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.
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.
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.pyKey concepts
- Virtual environment (venv): a folder holding a project's own
pipand 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'sbin/folder first inPATHfor 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.venvfolder.- Externally managed environment: the protection on modern Linux that blocks
system-wide
pipinstalls, 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.