Skip to content
Home
Python Virtual Environments: A Complete Guide

Python Virtual Environments: A Complete Guide

Python Python 7 min read 1457 words Beginner ExcellentWiki Editorial Team

Python virtual environments isolate project dependencies so you don’t end up with conflicting package versions. They are one of the most fundamental tools in Python development — every professional Python developer uses them for every project. Working without virtual environments leads to dependency hell, where installing one project’s requirements breaks another project’s setup.

Why Use Virtual Environments?

  • Project A needs Django 4.2, Project B needs Django 5.0
  • Without isolation, you can only have one version installed system-wide
  • Virtual environments solve this by giving each project its own package directory

The problem becomes even more acute when you manage multiple projects with different dependency trees. A Django 4.2 project might require specific sub-dependency versions that are incompatible with a FastAPI project on the same machine. Virtual environments keep each project’s ecosystem completely separate.

Beyond dependency isolation, virtual environments make your projects reproducible. When you run pip freeze > requirements.txt inside an environment, you capture exactly what that project needs — not the hundreds of packages you might have installed globally over years of development.

Creating a Virtual Environment

Python 3.3+ includes venv out of the box:

# Create the environment
python3 -m venv venv

# Activate it
source venv/bin/activate      # Linux / macOS
venv\Scripts\activate          # Windows (Command Prompt)
# .\venv\Scripts\Activate.ps1  # Windows (PowerShell)

Once activated, your prompt shows (venv):

(venv) ~/project $

The first venv in python3 -m venv venv is the module name, and the second venv is the directory name where the environment will be created. You can name it anything — .venv is another popular convention, especially since some editors and tools auto-detect environments named .venv.

Installing Packages

Once your environment is activated, pip installs packages into the environment’s private directory rather than the system-wide site-packages:

# Inside the activated environment
pip install requests
pip install django==4.2
pip install "fastapi>=0.100,<1.0"

# Install from requirements file
pip install -r requirements.txt

Version pinning is important. pip install django installs the latest version, which may introduce breaking changes when you rebuild your environment later. Always pin major versions (django==4.2) or use version ranges (fastapi>=0.100,<1.0) to control what gets installed.

Freezing Dependencies

pip freeze > requirements.txt

This saves all installed packages and versions. Share this file so others can replicate your environment. The resulting file lists every package installed in the environment, including sub-dependencies — not just the ones you explicitly installed. This ensures full reproducibility.

Deactivating

deactivate

This restores your shell to the system Python and PATH. You can also simply close the terminal — the environment is deactivated automatically.

Requirements File Format

# requirements.txt
django==4.2.16
requests==2.31.0
pytest>=7.0,<8.0
fastapi>=0.100.0

Common categories:

# requirements-dev.txt (development only)
pytest==7.4.0
black==23.9.0
ruff==0.1.0

Splitting requirements into production and development files is a best practice. Your production image only needs Django and Requests — it does not need testing tools, linters, or type checkers. Tools like pip-compile from the pip-tools package can help manage these split requirement files automatically.

Environment Managers Compared

Beyond the built-in venv module, several third-party tools offer enhanced dependency management. Poetry provides lock files (like npm’s package-lock.json), ensuring deterministic installs across environments. Pipenv combines pip and virtualenv with a Pipfile format. Conda handles non-Python native dependencies — critical for data science stacks that depend on compiled libraries like BLAS, CUDA, and OpenCV. For Docker-based deployments, many teams skip virtual environments entirely and rely on container isolation, using pip install directly in the Dockerfile.

Lock Files and Reproducible Builds

A requirements.txt created with pip freeze captures the exact versions of all installed packages, including transitive dependencies. However, pip does not have a built-in lock file mechanism like npm’s lockfile. Tools like pip-tools provide pip-compile which generates a locked requirements.txt from loosely specified dependencies in requirements.in. This gives you the best of both worlds: flexible dependency declarations with deterministic installs.

Common Workflow

Here is the standard workflow you will use for every Python project:

# Start a new project
mkdir myproject && cd myproject
python3 -m venv venv
source venv/bin/activate

# Install deps as you go
pip install flask
pip freeze > requirements.txt

# Later, another developer sets up
git clone repo && cd repo
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

This workflow ensures that every developer on your team, every CI server, and every deployment target gets exactly the same dependencies. The only variable is the Python runtime version — which is why you should also specify the Python version in your project’s pyproject.toml or runtime.txt.

Virtual Environment Location

By convention, the environment is named venv or .venv and placed in the project root. Add it to .gitignore:

# .gitignore
venv/
.env
__pycache__/
*.pyc

Never commit your virtual environment to version control. It can contain platform-specific binaries, is often hundreds of megabytes, and is fully reproducible from requirements.txt. Instead, document the Python version required and let each developer or deployment create their own environment.

Alternatives to venv

ToolDescription
venvBuilt-in, lightweight, standard
virtualenvOlder, more features (works with Python 2)
pipenvCombines pip + venv + Pipfile
poetryModern dependency management + packaging
condaData science focused, handles non-Python deps

For most projects, the built-in venv module is sufficient. Consider Poetry when you need full package publishing support, or Conda when working with data science libraries that have complex native dependencies (NumPy, SciPy, TensorFlow).

Common Issues

“command not found: pip”

python3 -m pip install requests

Pip is outdated

pip install --upgrade pip

Activate in a script

#!/bin/bash
source venv/bin/activate
python script.py

Or better, use the venv’s Python directly:

#!/bin/bash
venv/bin/python script.py

Using venv/bin/python directly is often preferred in scripts and cron jobs because it avoids the need to activate the environment. The Python interpreter inside the environment automatically uses the environment’s site-packages.


Related: Check our pip command fix guide for pip troubleshooting.

Using pip-tools for Dependency Management

The pip-tools package (pip install pip-tools) provides two commands: pip-compile and pip-sync. Define your direct dependencies in a requirements.in file, then run pip-compile requirements.in to generate a requirements.txt with pinned transitive dependencies. This gives you the reproducibility of a lock file with the simplicity of declaring only your direct dependencies. Run pip-sync to ensure your environment exactly matches the compiled requirements, removing packages that are not listed. This workflow is less complex than Poetry but more rigorous than raw pip.

Environment Variables and .env Files

Store configuration outside your code using environment variables. The python-dotenv package loads variables from a .env file: from dotenv import load_dotenv; load_dotenv(). Never commit .env files to version control — add .env to .gitignore. Use os.getenv('DATABASE_URL') or os.environ.get('API_KEY') to access variables at runtime. Combine with environment-specific .env.production, .env.staging, .env.development files loaded conditionally. This pattern keeps secrets out of your codebase while maintaining per-environment configuration flexibility.

Environment Management Workflows

A common team workflow: each developer creates their own virtual environment from the project’s requirements.txt. Use pip freeze > requirements.txt to capture exact versions after testing. For reproducible builds, add a constraints.txt file that pins transitive dependencies. CI/CD pipelines recreate the environment from scratch using pip install -r requirements.txt. For projects with conflicting dependencies (Python 2 vs 3, different framework versions), Docker containers provide stronger isolation than virtual environments alone.

FAQ

Should I commit my virtual environment to version control?

No — never commit venv/ or .venv/ to version control. It contains platform-specific binaries, is large, and is fully reproducible from requirements.txt. Add it to .gitignore and let each developer recreate it.

What is the difference between venv and virtualenv?

venv is a built-in module (Python 3.3+) with basic functionality. virtualenv is a third-party package that works with Python 2 and 3, supports faster creation with --system-site-packages, and has more advanced features. For modern Python 3 projects, venv is the recommended default.

How do I use different Python versions per environment?

Specify the Python interpreter when creating the environment: python3.11 -m venv venv creates an environment using Python 3.11. Use pyenv to manage multiple Python versions on your system and switch between them per project.

Resolving Dependency Conflicts

When projects grow, dependency conflicts become inevitable — package A requires requests>=2.25 while package B pins requests==2.24. Use pip check to verify installed packages have compatible dependencies. For complex dependency resolution, tools like poetry and pipenv use SAT solvers to find compatible version sets. A practical approach: periodically update your lock file by deleting it and regenerating from scratch, then test thoroughly. Consider pinning only direct dependencies and letting transitive dependencies resolve to their latest compatible versions.

What is the difference between requirements.txt and Pipfile?

requirements.txt is a simple list of packages. Pipfile (used by Pipenv) is a TOML file that separates main and dev dependencies, includes hashes for security, and generates a Pipfile.lock for deterministic installs. Poetry uses pyproject.toml + poetry.lock for the same purpose.

For a comprehensive overview, read our article on Pip Command Not Found.

Section: Python 1457 words 7 min read Beginner 756 articles in section Report inaccuracy Back to top