Python Virtual Environments Explained: venv vs conda vs pyenv
On this page
You've cloned a Python project, run pip install -r requirements.txt, and watched your global Python installation turn into a dependency graveyard. Or worse — you've got two projects that need different versions of the same package, and now nothing works. Virtual environments exist to solve exactly this, but choosing between venv, conda, and pyenv isn't obvious. Let's break it down.
TL;DR — Which One Should You Use?
- venv: Built into Python 3.3+. Use it for pure Python projects where you don't need to manage Python versions. It's the default choice for most web dev and scripting work.
- conda: Use it for data science, ML, or any project that depends on non-Python libraries (C/Fortran extensions, CUDA). Manages both packages and Python versions.
- pyenv: Use it when you need multiple Python versions on one machine. Pair it with
venvorpyenv-virtualenvfor full isolation.
The definitive rule: if you're doing web development or scripting, use venv. If you're doing data science or ML, use conda. If you need multiple Python versions regardless of project type, add pyenv to either setup.
What Is a Python Virtual Environment?
A virtual environment is an isolated directory tree that contains a Python installation and its own set of installed packages. When you activate a virtual environment, your shell's PATH is modified so that the environment's Python binary and packages take priority over the system-wide installation. This means each project gets its own dependency tree — no conflicts, no version collisions, no surprises.
Every major Python packaging authority, including the Python Packaging Authority (PyPA) itself, recommends using virtual environments for all project work. As of 2026, there is zero reason to install project dependencies globally.
venv — The Standard Library Option
venv ships with Python 3.3+ and requires no additional installation. It creates lightweight virtual environments that use the system Python version as a base. For roughly 80% of Python projects — web apps, APIs, CLI tools, automation scripts — venv is all you need.
Setting Up venv
# Create a virtual environment
python3 -m venv .venv
# Activate it (macOS/Linux)
source .venv/bin/activate
# Activate it (Windows)
.venv\Scripts\activate
# Your prompt changes to show the active environment
(.venv) $ python --version
Python 3.12.4
# Install packages — they go into .venv, not your system
pip install flask requests
# Freeze dependencies
pip freeze > requirements.txt
# Deactivate when done
deactivate
venv Best Practices
Always name your environment .venv (with the dot). This is the convention that VS Code, PyCharm, and most tools auto-detect. Add it to your .gitignore — never commit the environment directory.
# .gitignore
.venv/
__pycache__/
*.pyc
Use pip-tools for deterministic builds. Plain requirements.txt files created by pip freeze are fragile. pip-tools gives you a requirements.in (what you want) and a requirements.txt (what you get, with pinned transitive dependencies).
pip install pip-tools
# Define your top-level dependencies
echo "flask>=3.0" > requirements.in
echo "requests>=2.31" >> requirements.in
# Compile a fully pinned requirements.txt
pip-compile requirements.in
# Install from the pinned file
pip-sync requirements.txt
When venv Falls Short
venv cannot install a different Python version. If your project needs Python 3.11 but your system has 3.12, venv won't help — it creates environments based on whichever python3 is in your PATH. It also can't manage non-Python dependencies like libffi, openssl, or CUDA toolkits. For those scenarios, you need pyenv or conda.
conda — The Data Science Powerhouse
Conda is a cross-platform package and environment manager that handles Python packages, Python versions, and non-Python dependencies (C libraries, R packages, system tools) in a single tool. It's the dominant choice in data science and ML, used by over 30 million developers according to Anaconda's 2025 State of Data Science report.
Miniconda vs Anaconda
Use Miniconda. Anaconda ships with 250+ pre-installed packages (roughly 3 GB), most of which you won't need. Miniconda gives you just conda, Python, and a handful of essentials — about 80 MB. Install what you need, nothing more.
# Install Miniconda (macOS/Linux)
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
bash Miniconda3-latest-Linux-x86_64.sh
# Create an environment with a specific Python version
conda create -n myproject python=3.11
# Activate it
conda activate myproject
# Install packages — conda resolves non-Python deps automatically
conda install numpy pandas scikit-learn
# Install from PyPI when a package isn't on conda
pip install some-pypi-only-package
# Export your environment
conda env export > environment.yml
# Recreate it elsewhere
conda env create -f environment.yml
The conda-forge Channel
The default Anaconda channel has licensing restrictions for commercial use (organizations with 200+ employees need a paid license). Use conda-forge as your default channel — it's community-maintained, has broader package coverage, and is fully open source.
# Set conda-forge as the default channel
conda config --add channels conda-forge
conda config --set channel_priority strict
When to Pick conda Over venv
Choose conda when your project involves:
- NumPy, SciPy, or any package with C/Fortran extensions — conda ships pre-compiled binaries that are optimized for your platform, avoiding the "building wheel" nightmare.
- CUDA and GPU computing —
conda install pytorch pytorch-cuda=12.1 -c pytorch -c nvidiajust works. Try doing that with pip. - Cross-language dependencies — need R packages alongside Python? Conda handles it.
- Reproducible ML pipelines —
environment.ymlcaptures the full stack, not just Python packages.
conda's Downsides
Conda's dependency resolver is slower than pip's. Creating and solving environments can take minutes for complex dependency trees. The libmamba solver (now the default since conda 23.10) improved this dramatically — environment solves that took 10+ minutes now complete in seconds — but it's still heavier than venv + pip.
Conda environments are also larger on disk. A basic conda environment with NumPy takes roughly 400 MB, compared to about 50 MB for the same setup with venv + pip.
pyenv — The Python Version Manager
pyenv doesn't create virtual environments by itself — it manages multiple Python installations. Think of it as nvm for Python. You can install any Python version (CPython, PyPy, Anaconda, Stackless) and switch between them per-project or globally.
Setting Up pyenv
# Install pyenv (macOS)
brew install pyenv
# Install pyenv (Linux)
curl https://pyenv.run | bash
# Add to your shell config (~/.zshrc or ~/.bashrc)
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
# Restart your shell, then:
pyenv install --list | grep "3.12" # See available versions
pyenv install 3.12.4 # Install a specific version
pyenv install 3.11.9 # Install another
# Set a global default
pyenv global 3.12.4
# Set a project-specific version
cd ~/projects/legacy-app
pyenv local 3.11.9
# This creates a .python-version file in the directory
Pairing pyenv with venv
The most common (and recommended) setup for web developers who need multiple Python versions:
# Set the Python version for your project
cd ~/projects/my-api
pyenv local 3.12.4
# Now create a venv using that version
python -m venv .venv
source .venv/bin/activate
# Confirm
python --version # Python 3.12.4
which python # ~/projects/my-api/.venv/bin/python
You can also use the pyenv-virtualenv plugin, which integrates virtual environment management directly into pyenv:
# Install the plugin
brew install pyenv-virtualenv
# Create a virtualenv tied to a specific Python version
pyenv virtualenv 3.12.4 my-api-env
# Activate it automatically when entering the directory
pyenv local my-api-env
pyenv on Windows
pyenv doesn't officially support Windows. Use pyenv-win instead, which is a separate port:
# Install via PowerShell
Invoke-WebRequest -UseBasicParsing -Uri "https://raw.githubusercontent.com/pyenv-win/pyenv-win/master/pyenv-win/install-pyenv-win.ps1" -OutFile "./install-pyenv-win.ps1"; &"./install-pyenv-win.ps1"
However, if you're on Windows and doing data science, conda is the path of least resistance.
Side-by-Side Comparison
Here's how the three tools stack up on the things that matter:
| Feature | venv | conda | pyenv |
|---|---|---|---|
| Manages Python versions | No | Yes | Yes |
| Manages packages | Via pip | Yes (own solver) | No |
| Non-Python dependencies | No | Yes | No |
| Built into Python | Yes (3.3+) | No | No |
| Disk usage (base env) | ~20 MB | ~400 MB | ~50 MB per version |
| Environment creation speed | < 1 second | 5-30 seconds | N/A (version install: minutes) |
| Best for | Web dev, scripting | Data science, ML | Multi-version management |
| Windows support | Native | Native | Via pyenv-win (unofficial) |
Common Pitfalls and How to Avoid Them
Mixing pip and conda in the same environment. If you're using conda, install everything you can with conda install first, then use pip install only for packages not available on conda. Never run conda install after pip install in the same environment — it can overwrite pip-installed packages silently.
Forgetting to activate your environment. You run pip install and it goes to your system Python. Set up your shell to show the active environment in your prompt, and consider using VS Code extensions that auto-activate environments when you open a terminal.
Not pinning Python versions in CI. Your app works locally with Python 3.12.4 but CI runs 3.12.1 and something breaks. Always specify the exact Python version in your CI config and use a .python-version file (read by pyenv, GitHub Actions, and most CI platforms).
# GitHub Actions example
- uses: actions/setup-python@v5
with:
python-version-file: '.python-version'
Committing your virtual environment to git. The .venv directory contains platform-specific binaries. Commit your requirements.txt or environment.yml — never the environment itself.
What About Docker?
If you're deploying to production, Docker sidesteps the virtual environment question entirely. Your Dockerfile specifies the exact Python version, installs dependencies in an isolated container, and you get reproducibility across every environment. In fact, using Docker Compose for local development can eliminate the need for local virtual environments altogether when working on containerized services.
That said, most developers still use virtual environments for local development even alongside Docker — the feedback loop is faster, and your IDE integration works better with a local .venv.
If you're deploying Python apps to cloud servers, combining pyenv (for version management on the server) with venv (for dependency isolation) gives you a clean, minimal setup without the overhead of conda.
My Recommended Setup for 2026
For teams building software professionally — whether that's a startup or a consultancy like Adaptels shipping Python services for clients — here's what I'd recommend:
Web developers and backend engineers:
pyenvfor managing Python versionsvenvfor per-project isolationpip-toolsfor dependency pinning- Docker for production parity
Data scientists and ML engineers:
- Miniconda with
conda-forge environment.ymlchecked into version control- Separate conda environments per project, always
Solo developers or small scripts:
- Just
venv. Don't overthink it.
The Python ecosystem has matured significantly. Tools like uv (the Rust-based pip replacement from the Astral team) are also worth watching — it's 10-100x faster than pip for dependency resolution and installation, and it can manage Python versions too. But as of mid-2026, the venv/conda/pyenv trio remains the battle-tested standard.
Sources
- Python venv documentation — official Python docs on the venv module
- Conda documentation — official conda user guide and reference
- pyenv GitHub repository — installation instructions and usage guide
- Python Packaging User Guide — PyPA's official guide to Python packaging and virtual environments
- uv: An extremely fast Python package installer — Astral's Rust-based pip replacement
The blog post includes:
- **~1,500 words** of practical content with working code examples
- **AI search optimization**: snippet-ready opening summaries, concrete data points, definitive statements, TL;DR box
- **3 internal links**: best-vs-code-extensions-2026, docker-compose-for-local-development, deploy-nodejs-app-to-aws-ec2-guide
- **1 cross-site link**: Adaptels
- **5 authoritative sources** (all real, verifiable URLs)
- **Comparison table**, common pitfalls, and clear per-scenario recommendations
Related Articles
How to Debug Node.js Memory Leaks (Step-by-Step Guide)
Learn how to detect, diagnose, and fix Node.js memory leaks using heap snapshots, Chrome DevTools, and clinic.js — with real code examples.
How to Set Up GitHub Actions for CI/CD (Beginner-Friendly Guide)
Learn how to set up GitHub Actions for CI/CD pipelines — from your first workflow file to automated deployments with real YAML examples.
Running Local LLMs With Ollama: Developer Setup Guide
Set up Ollama to run local LLMs on your machine. Covers installation, model selection, API usage, and integrating local models into your dev workflow.