Python virtual environments solve one of the most common problems in Python development: dependency conflicts between projects. When Project A needs Django 4.2 and Project B needs Django 5.0, installing both globally breaks one of them. Virtual environments create isolated Python installations where each project has its own packages, its own versions, and its own pip — completely independent from each other and from your system Python. This guide covers everything from creating your first environment through production deployment patterns.
Prerequisites
- Python 3.3 or later installed (
python3 --versionto check) - Basic familiarity with the command line (terminal, shell, or PowerShell)
- A text editor or IDE for Python development
pipavailable (included with Python 3.4+ by default)
Creating Virtual Environments with venv
venv is Python’s built-in module for creating virtual environments. It ships with Python 3.3+ and requires no additional installation.
Basic Creation
# Create a virtual environment in the .venv directory
python3 -m venv .venv
# On some systems, you may need to install the venv package first
# Debian/Ubuntu:
sudo apt install python3-venv
# Verify the environment was created
ls .venv/
# Output: bin/ include/ lib/ pyvenv.cfg
The .venv directory name is a convention — you can name it anything, but .venv is the most common choice. The leading dot keeps it hidden in Unix directory listings and most .gitignore templates already exclude it.
What venv Creates
.venv/
├── bin/ # Activation scripts and executables (Linux/macOS)
│ ├── activate # Bash activation script
│ ├── activate.csh # C-shell activation
│ ├── activate.fish # Fish shell activation
│ ├── pip # Isolated pip
│ ├── pip3 # Symlink to pip
│ └── python3 # Symlink to the Python interpreter
├── include/ # C headers for building extensions
├── lib/python3.x/
│ └── site-packages/ # Where pip installs packages
└── pyvenv.cfg # Environment configuration
On Windows, the structure is similar but uses Scripts/ instead of bin/ and .exe extensions.
Activating the Environment
Activation modifies your shell’s PATH so that python and pip point to the virtual environment’s copies instead of the system-wide ones.
# Linux / macOS (Bash/Zsh)
source .venv/bin/activate
# Linux (Fish shell)
source .venv/bin/activate.fish
# Windows (Command Prompt)
.venv\Scripts\activate.bat
# Windows (PowerShell)
.venv\Scripts\Activate.ps1
When activated, your prompt changes to show the environment name:
(.venv) user@hostname:~/myproject$
Verify the environment is active:
# Check which Python is being used
which python3
# Output: /home/user/myproject/.venv/bin/python3
# Check which pip is being used
which pip
# Output: /home/user/myproject/.venv/bin/pip
# The system Python is untouched
python3 -c "import sys; print(sys.prefix)"
# Output: /home/user/myproject/.venv
Deactivating the Environment
deactivate
This restores your PATH to its previous state. The virtual environment remains on disk — deactivating does not delete anything.
Managing Packages with pip
With the virtual environment activated, pip installs packages only into that environment.
Installing Packages
# Install a specific package
pip install requests
# Install a specific version
pip install requests==2.31.0
# Install with a version constraint
pip install "requests>=2.28,<3.0"
# Install multiple packages
pip install flask sqlalchemy redis
# Upgrade a package
pip install --upgrade requests
# Install from a Git repository
pip install git+https://github.com/user/repo.git@main
Listing and Inspecting Packages
# List all installed packages
pip list
# Show detailed info about a package
pip show requests
# Check for outdated packages
pip list --outdated
# Show dependency tree (install pipdeptree first)
pip install pipdeptree
pipdeptree
Uninstalling Packages
# Remove a single package
pip uninstall requests
# Remove a package without confirmation prompt
pip uninstall -y requests
Working with requirements.txt
requirements.txt is the standard way to declare and share Python project dependencies. It lists packages and their versions so anyone can recreate the same environment.
Generating requirements.txt
# Export all installed packages with exact versions
pip freeze > requirements.txt
The output looks like this:
certifi==2024.2.2
charset-normalizer==3.3.2
idna==3.6
requests==2.31.0
urllib3==2.2.1
Installing from requirements.txt
# Create a new environment and install dependencies
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
Best Practices for requirements.txt
# Pin exact versions for production (reproducible builds)
requests==2.31.0
flask==3.0.2
# Use >= for library development (allow compatible updates)
requests>=2.28
# Separate dev dependencies into a second file
# requirements-dev.txt:
-r requirements.txt
pytest==8.0.0
black==24.2.0
mypy==1.8.0
# Install dev dependencies (includes production deps too)
pip install -r requirements-dev.txt
Real-World Scenario: Dependency Conflict Resolution
You join a team working on a web application. You clone the repo, create a virtual environment, and run pip install -r requirements.txt. It fails with a version conflict:
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed.
Package A 2.0 requires LibX>=3.0, but you have LibX 2.5 which is incompatible.
How to fix it:
# See what requires the conflicting package
pip install pipdeptree
pipdeptree --reverse --packages LibX
# Option 1: Upgrade the conflicting package
pip install --upgrade LibX
# Option 2: Find compatible versions
pip install "PackageA>=2.0" "PackageB>=1.0" --dry-run
# Regenerate requirements.txt with the resolved versions
pip freeze > requirements.txt
Comparing Python Environment Tools
| Feature | venv | virtualenv | conda | Poetry |
|---|---|---|---|---|
| Built into Python | Yes (3.3+) | No (pip install) | No (separate installer) | No (pip install) |
| Speed | Moderate | Fast | Slow | Moderate |
| Python version management | No | No | Yes | No (use pyenv) |
| Non-Python dependencies | No | No | Yes (C libs, R, etc.) | No |
| Lock file support | No | No | environment.yml | poetry.lock |
| Dependency resolution | pip (basic) | pip (basic) | conda solver | Advanced |
| Disk usage per env | ~20 MB | ~20 MB | ~500 MB+ | ~20 MB |
| Best for | Standard Python projects | Legacy Python 2 support | Data science, ML, cross-language | Modern Python packages with strict deps |
Use venv for standard Python projects — it is the official tool, requires nothing extra, and works everywhere Python runs. Use conda when you need non-Python dependencies (NumPy with BLAS, CUDA, R packages). Use Poetry when you need strict dependency resolution and a modern workflow for publishing packages.
Virtual Environments in CI/CD and Docker
CI/CD Pipelines (GitHub Actions Example)
# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: |
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Run tests
run: |
source .venv/bin/activate
pytest
Docker (Multi-Stage Build)
# Build stage
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
RUN pip install --no-cache-dir -r requirements.txt
# Production stage
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
COPY . .
CMD ["python", "app.py"]
Using a virtual environment inside Docker is debatable — the container already provides isolation. However, multi-stage builds with venv produce smaller images because the build tools and pip cache stay in the builder stage.
Gotchas and Edge Cases
Python version mismatch: A virtual environment is tied to the Python version that created it. If you upgrade Python from 3.11 to 3.12, existing environments may break. Recreate them after major Python upgrades.
--system-site-packages leaks: Creating an environment with python3 -m venv --system-site-packages .venv gives access to globally installed packages. This is useful for packages that are hard to install via pip (like system-provided PyGObject), but it means pip freeze includes system packages, polluting your requirements.txt. Use pip freeze --local to list only environment-specific packages.
pip cache fills disk: pip caches downloaded packages in ~/.cache/pip/. On build servers, this grows unbounded. Use pip install --no-cache-dir or periodically run pip cache purge.
Activation is shell-specific: If you use source .venv/bin/activate in a script that runs in a subshell, the activation does not persist. Instead, call the Python binary directly: .venv/bin/python script.py or .venv/bin/pip install package.
Windows PowerShell execution policy: By default, PowerShell blocks running scripts including activation. Fix it once per user:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Shared network drives and virtual environments: Virtual environments use symlinks to the Python interpreter. Some network filesystems (SMB, NFS) do not support symlinks, causing the environment to fail. Create environments on local disk only.
Troubleshooting Common Issues
”No module named venv"
# Debian/Ubuntu — install the venv module
sudo apt install python3-venv
# Some distributions ship Python without venv
# Verify it is available:
python3 -m venv --help
"pip install” fails with permission errors
# If you see "Permission denied" — you probably forgot to activate
source .venv/bin/activate
pip install requests
# NEVER use sudo pip install — it modifies system packages
# If you need system-wide packages, use apt/dnf instead
Environment uses wrong Python version
# Specify the Python version when creating the environment
python3.12 -m venv .venv
# Or use the full path
/usr/bin/python3.12 -m venv .venv
# Verify the version inside the environment
source .venv/bin/activate
python --version
packages installed globally instead of in the environment
# Check that pip belongs to the environment
which pip
# Should show: .venv/bin/pip
# If it shows /usr/bin/pip — the environment is not activated
source .venv/bin/activate
Summary
python3 -m venv .venvcreates an isolated Python environment — each project gets its own packages without affecting other projects or the system Python- Activate with
source .venv/bin/activateto make the environment’s Python and pip the defaults in your current shell session pip freeze > requirements.txtcaptures exact dependency versions — share this file in version control so others can recreate the identical environmentpip install -r requirements.txtinside a fresh environment reproduces the dependency set — always pin versions with==for production deployments- Never use
sudo pip install— it modifies system packages and creates conflicts. Virtual environments eliminate the need for root access entirely - Recreate environments after Python upgrades — they are tied to the Python version that created them and may break on major version changes