Python Virtual Environments: Complete Guide to venv, pipenv, and Poetry
Every Python developer eventually encounters the same painful scenario: you install a package for one project and it breaks another. Project A needs Django 4.2 while Project B requires Django 5.0. A system-wide pip install overwrites the version you need elsewhere. Your colleague clones your repo and nothing works because they have different package versions. Virtual environments solve all of these problems by giving each project its own isolated Python installation with its own set of packages.
This guide covers every major tool for managing Python virtual environments in 2026. We start with the built-in venv module and pip, then move through pipenv, Poetry, conda, virtualenvwrapper, and pyenv. Each section includes real commands you can run immediately, along with the reasoning for when to choose each tool.
Table of Contents
- What Are Virtual Environments and Why They Matter
- venv: The Built-In Solution
- pip and requirements.txt
- pipenv: Pip and Virtualenv Combined
- Poetry: Modern Dependency Management
- Conda Environments
- virtualenvwrapper
- Working with Multiple Python Versions (pyenv)
- Comparison: venv vs virtualenv vs pipenv vs Poetry vs conda
- Best Practices for Project Structure
- Troubleshooting Common Issues
- Frequently Asked Questions
1. What Are Virtual Environments and Why They Matter
A Python virtual environment is a self-contained directory tree that contains a Python installation for a particular version of Python, plus additional packages. When you activate a virtual environment, the python and pip commands point to that environment's copies instead of the system-wide installation. Any package you install goes into the environment's site-packages directory, not the global one.
Without virtual environments, you face several real problems:
- Dependency conflicts — Project A requires
requests==2.28while Project B needsrequests==2.31. With a global installation, only one version can exist at a time. - Reproducibility — When you deploy to production or share your code, others need the exact same package versions. A virtual environment combined with a lock file makes this deterministic.
- System Python corruption — Many Linux distributions rely on the system Python for package management tools. Installing packages globally with pip can break
apt,yum, or other system utilities. - Permission issues — Installing packages globally often requires
sudo, which is a security risk. Virtual environments live in your user space and need no elevated privileges. - Clean project boundaries — Without isolation, you accumulate hundreds of packages globally and lose track of which project needs what.
The concept is simple: each project gets its own Python and its own packages. The implementation details vary between tools, but this core idea remains the same across venv, virtualenv, pipenv, Poetry, and conda.
2. venv: The Built-In Solution
The venv module is included in Python 3.3 and later. It is the officially recommended way to create virtual environments and requires no additional installation.
Creating a Virtual Environment
# Create a virtual environment in the current directory
python3 -m venv .venv
# Or with a different name
python3 -m venv myproject-env
# Create with access to system site-packages
python3 -m venv .venv --system-site-packages
# Create with a specific prompt name (shown in terminal)
python3 -m venv .venv --prompt myproject
The .venv naming convention is widely adopted because it is hidden by default on Unix systems, immediately recognizable, and most IDEs detect it automatically.
Activating and Deactivating
# Linux / macOS (bash/zsh)
source .venv/bin/activate
# Linux / macOS (fish shell)
source .venv/bin/activate.fish
# Windows (Command Prompt)
.venv\Scripts\activate.bat
# Windows (PowerShell)
.venv\Scripts\Activate.ps1
# After activation, your prompt changes:
# (.venv) user@host:~/myproject$
# Verify which Python is active
which python # /home/user/myproject/.venv/bin/python
# Deactivate when done
deactivate
When a virtual environment is active, the python and pip commands automatically point to the environment's binaries. You do not need to use python3 or pip3.
What Is Inside a Virtual Environment
.venv/
bin/ # activate scripts, python/pip symlinks
include/ # C header files for compiling extensions
lib/python3.12/
site-packages/ # all installed packages go here
pyvenv.cfg # config: home path, Python version, site-packages flag
The virtual environment is just a directory. You can delete it with rm -rf .venv and recreate it at any time. Environments are disposable and cheap to rebuild.
3. pip and requirements.txt
Once you have a virtual environment active, pip is how you install packages. The requirements.txt file records which packages your project depends on so others can reproduce the environment.
Installing Packages
# Install a single package (latest version)
pip install requests
# Install a specific version
pip install requests==2.31.0
# Install with version constraints
pip install "requests>=2.28,<3.0"
pip install "django~=4.2.0" # compatible release (4.2.x)
# Install from a requirements file
pip install -r requirements.txt
# Upgrade a package
pip install --upgrade requests
# Install in editable mode (for development)
pip install -e .
pip install -e ".[dev,test]" # with optional extras
Managing requirements.txt
# Generate requirements from current environment
pip freeze > requirements.txt
# Output looks like:
# certifi==2024.2.2
# charset-normalizer==3.3.2
# requests==2.31.0
# urllib3==2.2.1
# Install from requirements file
pip install -r requirements.txt
# Show installed packages / check outdated
pip list
pip list --outdated
# Uninstall a package
pip uninstall requests
Separate Requirements Files
# requirements.txt (production)
flask==3.0.2
sqlalchemy==2.0.28
gunicorn==21.2.0
# requirements-dev.txt (development)
-r requirements.txt
pytest==8.0.2
black==24.2.0
ruff==0.3.0
mypy==1.8.0
# Install for development
pip install -r requirements-dev.txt
pip-tools: Better Requirements Management
# Install pip-tools
pip install pip-tools
# Create requirements.in with direct dependencies only
# requirements.in: flask, sqlalchemy, gunicorn
# Compile pinned requirements with all transitive dependencies
pip-compile requirements.in
# Sync your environment to match requirements.txt exactly
pip-sync requirements.txt
# Upgrade a specific package
pip-compile --upgrade-package flask requirements.in
pip-tools bridges the gap between manual pip freeze and full-featured tools like Poetry. You maintain direct dependencies in .in files, and pip-compile resolves and pins all transitive dependencies automatically.
4. pipenv: Pip and Virtualenv Combined
Pipenv combines pip and virtualenv into a single workflow. It automatically creates and manages virtual environments, uses a Pipfile instead of requirements.txt, and generates a Pipfile.lock for deterministic builds.
# Install pipenv
pip install pipenv
# Create a new project with a virtual environment
mkdir myproject && cd myproject
pipenv install
# Install packages (creates Pipfile and Pipfile.lock)
pipenv install requests flask
# Install development dependencies
pipenv install --dev pytest black ruff
# Activate the virtual environment shell
pipenv shell
# Run a command without activating
pipenv run python app.py
pipenv run pytest
# Exit the pipenv shell
exit
The Pipfile
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
requests = "*"
flask = ">=3.0"
[dev-packages]
pytest = "*"
black = "*"
[requires]
python_version = "3.12"
Common pipenv Commands
pipenv install # install from Pipfile
pipenv install --dev # include dev dependencies
pipenv update # update all packages
pipenv graph # show dependency graph
pipenv check # check for vulnerabilities
pipenv requirements > requirements.txt # export
pipenv uninstall requests # remove a package
pipenv --rm # remove the virtual environment
pipenv --venv # show venv location
pipenv --py # show Python path
Pipenv works well for application development with a straightforward workflow and lock file for reproducibility. However, it does not support building and publishing Python packages, and its dependency resolution can be slow for large projects.
5. Poetry: Modern Dependency Management
Poetry is the most feature-complete dependency management tool in the Python ecosystem as of 2026. It handles virtual environments, dependency resolution, lock files, package building, and publishing to PyPI — all from a single pyproject.toml file.
Installing and Configuring Poetry
# Official installer (recommended)
curl -sSL https://install.python-poetry.org | python3 -
# Or with pipx
pipx install poetry
# Configure Poetry to create venvs inside the project directory
poetry config virtualenvs.in-project true
Creating and Managing Projects
# Create a new project from scratch
poetry new myproject
# Or initialize in an existing project
cd existing-project
poetry init
# Add dependencies
poetry add requests flask sqlalchemy
# Add development dependencies
poetry add --group dev pytest black ruff mypy
# Add with version constraints
poetry add "django>=4.2,<5.0"
# Install all dependencies (creates poetry.lock)
poetry install
# Install without dev dependencies (production)
poetry install --without dev
# Update dependencies
poetry update
poetry remove flask
The pyproject.toml File
[tool.poetry]
name = "myproject"
version = "0.1.0"
description = "A sample Python project"
authors = ["Your Name <your@email.com>"]
[tool.poetry.dependencies]
python = "^3.10"
requests = "^2.31"
flask = "^3.0"
[tool.poetry.group.dev.dependencies]
pytest = "^8.0"
black = "^24.2"
[tool.poetry.scripts]
myapp = "myproject.cli:main"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Running Commands
poetry run python app.py # run in venv
poetry run pytest # run tests
poetry shell # activate shell
poetry env info # show env details
poetry show --tree # dependency tree
poetry export -f requirements.txt --output requirements.txt
poetry build # create .whl and .tar.gz
poetry publish # publish to PyPI
Poetry's advantages over pipenv: faster dependency resolution, package building and publishing, dependency groups, a single pyproject.toml following PEP 621, and more active development. For new projects in 2026, Poetry is the recommended choice.
6. Conda Environments
Conda is a cross-platform package and environment manager from Anaconda or Miniconda. Unlike venv and pip, conda can manage non-Python dependencies (C libraries, R packages, system tools) and install different Python versions per environment. It is the standard tool in data science.
# Install Miniconda (minimal installer)
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 --name myproject python=3.12
# Activate / deactivate
conda activate myproject
conda deactivate
# Install packages
conda install numpy pandas scikit-learn matplotlib
conda install -c conda-forge jupyterlab
# List environments
conda env list
# Export / recreate environment
conda env export > environment.yml
conda env create -f environment.yml
# Export only explicitly installed packages (cleaner)
conda env export --from-history > environment.yml
# Remove an environment
conda env remove --name myproject
The environment.yml File
name: myproject
channels:
- conda-forge
- defaults
dependencies:
- python=3.12
- numpy=1.26
- pandas=2.2
- scikit-learn=1.4
- pip:
- flask==3.0.2
- custom-package==1.0.0
The pip: section allows pip-only packages not available in conda channels. Always install conda packages first, then pip packages. Conda is the right choice for data science, ML, or scientific computing with complex C/Fortran dependencies. For web development, venv with pip or Poetry is simpler.
7. virtualenvwrapper
Virtualenvwrapper provides convenience shell commands for managing virtual environments, storing all environments in a central location rather than inside each project directory.
# Install and configure
pip install virtualenvwrapper
# Add to ~/.bashrc or ~/.zshrc:
export WORKON_HOME=$HOME/.virtualenvs
export PROJECT_HOME=$HOME/projects
export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
source /usr/local/bin/virtualenvwrapper.sh
# Core commands
mkvirtualenv myproject # create environment
mkvirtualenv myproject --python=python3.11 # specific version
workon # list environments
workon myproject # activate
deactivate # deactivate
rmvirtualenv myproject # remove
mkproject myproject # create project + env
cdproject # cd to project dir
cpvirtualenv myproject myproject-copy # clone
Virtualenvwrapper shines when you work on many projects and want quick switching with workon projectname. However, Poetry and pipenv handle environment management automatically, making virtualenvwrapper less necessary for modern workflows.
8. Working with Multiple Python Versions (pyenv)
Pyenv lets you install and switch between multiple Python versions on the same machine. It intercepts Python commands using shim executables and redirects them to the correct version based on your configuration.
Installing pyenv
# Install on Linux/macOS
curl https://pyenv.run | bash
# Add to ~/.bashrc or ~/.zshrc:
export PYENV_ROOT="$HOME/.pyenv"
[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
# On macOS with Homebrew
brew install pyenv
# Install build dependencies (Ubuntu/Debian)
sudo apt install -y build-essential libssl-dev zlib1g-dev \
libbz2-dev libreadline-dev libsqlite3-dev curl git \
libncursesw5-dev xz-utils tk-dev libxml2-dev \
libxmlsec1-dev libffi-dev liblzma-dev
Managing Python Versions
# List available / install specific versions
pyenv install --list | grep "^ 3\."
pyenv install 3.12.1
pyenv install 3.11.7
# Set global default / project-local / shell-session version
pyenv global 3.12.1
pyenv local 3.11.7 # creates .python-version file
pyenv shell 3.10.13
# List installed versions / check active version
pyenv versions
pyenv version
pyenv + venv Workflow
# Set the Python version for your project
cd ~/projects/myproject
pyenv local 3.12.1
# Create a virtual environment using that version
python -m venv .venv
source .venv/bin/activate
# Now you have an isolated environment running Python 3.12.1
pip install flask requests
pyenv-virtualenv Plugin
# Install the plugin
git clone https://github.com/pyenv/pyenv-virtualenv.git \
$(pyenv root)/plugins/pyenv-virtualenv
eval "$(pyenv virtualenv-init -)"
# Create a virtual environment tied to a Python version
pyenv virtualenv 3.12.1 myproject-env
# Activate or set as local environment
pyenv activate myproject-env
pyenv local myproject-env # auto-activates on cd
# List / deactivate / delete
pyenv virtualenvs
pyenv deactivate
pyenv virtualenv-delete myproject-env
The pyenv local command is powerful: when you cd into a directory with a .python-version file, pyenv automatically switches to the correct Python version. No manual activation needed.
9. Comparison: venv vs virtualenv vs pipenv vs Poetry vs conda
| Feature | venv | virtualenv | pipenv | Poetry | conda |
|---|---|---|---|---|---|
| Built into Python | Yes (3.3+) | No | No | No | No |
| Lock file | No (use pip-tools) | No | Pipfile.lock | poetry.lock | No (conda-lock) |
| Dependency resolution | Basic | Basic | Good | Excellent | Excellent |
| Manage Python versions | No | No | No | No | Yes |
| Non-Python packages | No | No | No | No | Yes |
| Build and publish | No | No | No | Yes | Yes |
| Config file | None | None | Pipfile | pyproject.toml | environment.yml |
| Speed (env creation) | Fast | Very fast | Moderate | Fast | Slow |
| Best for | Simple projects | Python 2 | Applications | Libraries + apps | Data science |
Decision Guide
- Small scripts, learning, or simple projects →
python -m venv+pip+requirements.txt - Web applications, APIs, microservices → Poetry or pipenv
- Python libraries or packages for PyPI → Poetry (handles building and publishing)
- Data science, ML, scientific computing → Conda (manages C libs, CUDA, etc.)
- Multiple Python versions required → pyenv + any of the above
- Legacy Python 2 projects → virtualenv
10. Best Practices for Project Structure
Recommended Project Layout
myproject/
.venv/ # virtual environment (NOT in git)
.python-version # pyenv version file (optional)
.gitignore
pyproject.toml # project metadata and dependencies
README.md
src/
myproject/
__init__.py
main.py
models.py
tests/
test_main.py
conftest.py
scripts/
seed_db.py
.gitignore for Virtual Environments
# Virtual environments
.venv/
venv/
env/
# Python
__pycache__/
*.py[cod]
*.egg-info/
dist/
build/
# IDE
.vscode/
.idea/
Key Best Practices
- Always use a virtual environment — even for small scripts. It costs nothing and prevents problems later.
- Use
.venvinside the project directory — discoverable by IDEs and clearly associated with the project. - Pin your dependencies — use exact versions in production. Use
poetry.lockorPipfile.lockfor reproducibility. - Separate dev and production dependencies — testing frameworks and linters should not be in production.
- Never commit the virtual environment — add it to
.gitignore. Commit lock files instead. - Recreate rather than repair — if an environment gets corrupted, delete it and rebuild from your lock file.
- Use
python -m pipinstead of barepip— ensures you use the pip associated with the active interpreter. - Document the setup process — include clear instructions in your README for creating the environment.
11. Troubleshooting Common Issues
ModuleNotFoundError After Installing a Package
# Check which Python/pip you are actually running
which python # should point to .venv/bin/python
which pip # should point to .venv/bin/pip
# If they point to /usr/bin/python, your venv is not activated
source .venv/bin/activate
# Verify the package is installed
pip list | grep package-name
# IDE: make sure it uses the venv Python
# VS Code: Ctrl+Shift+P > Python: Select Interpreter
# PyCharm: Settings > Project > Python Interpreter
Permission Denied Errors
# NEVER use sudo with pip in a virtual environment
# Wrong:
sudo pip install requests
# Right: activate the venv first
source .venv/bin/activate
pip install requests
# If the venv has permission issues, recreate it
rm -rf .venv && python3 -m venv .venv && source .venv/bin/activate
PowerShell Execution Policy (Windows)
# If you see "running scripts is disabled on this system":
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
.venv\Scripts\Activate.ps1
Python Version Mismatch
# The venv is tied to the Python version that created it
# If you upgrade Python and the old version is removed, the venv breaks
cat .venv/pyvenv.cfg | grep version
# Recreate if the version does not match:
rm -rf .venv && python3 -m venv .venv
source .venv/bin/activate && pip install -r requirements.txt
Conda and pip Conflict
# Always install conda packages FIRST, then pip-only packages
conda install numpy pandas scikit-learn
pip install some-pip-only-package
# Never install a conda package with pip or vice versa
# If things break, recreate from environment.yml
conda env remove --name myproject
conda env create -f environment.yml
"Externally Managed Environment" Error (PEP 668)
# Python 3.11+ on Debian/Ubuntu protects the system Python
# Solution: always use a virtual environment
python3 -m venv .venv
source .venv/bin/activate
pip install requests # works fine inside the venv
# Or use pipx for global CLI tools
pipx install black
pipx install ruff
Frequently Asked Questions
What is the difference between venv and virtualenv?
venv is the built-in virtual environment module included in Python 3.3 and later. It requires no extra installation. virtualenv is a third-party package that predates venv and works with Python 2 and 3. virtualenv is faster at creating environments and has additional features like seed packages. For most modern Python 3 projects, venv is sufficient. Use virtualenv if you need Python 2 support or its advanced features.
Should I commit my virtual environment folder to Git?
No, never commit a virtual environment folder to version control. They contain platform-specific binaries and can be hundreds of megabytes. Instead, commit your requirements.txt, Pipfile, pyproject.toml, or poetry.lock file so that others can recreate the environment. Add .venv/ to your .gitignore file.
How do I choose between pipenv, Poetry, and plain pip with venv?
Use plain pip with venv for simple scripts and small projects. Use pipenv for applications where you want automatic environment management and a lock file. Use Poetry for libraries or applications needing robust dependency resolution and the ability to build and publish packages. Poetry is the most feature-complete option and the community standard for serious Python projects in 2026.
Why does activating a virtual environment not work in my IDE?
Most IDEs have their own interpreter selection mechanism. In VS Code, press Ctrl+Shift+P and search for "Python: Select Interpreter," then choose .venv/bin/python. In PyCharm, go to Settings → Project → Python Interpreter and add the interpreter from your virtual environment. The integrated terminal should activate automatically once the IDE knows about the environment.
Can I use conda and pip together in the same environment?
Yes, but with caution. Install as many packages as possible with conda first, then use pip only for packages not in conda channels. Never use pip to install a package previously installed with conda. If you mix the two, conda may not track pip-installed packages correctly, causing dependency conflicts during future updates.
How do I manage multiple Python versions on the same machine?
Use pyenv to install and switch between versions. Run pyenv install 3.12.1, then pyenv global 3.12.1 for the default or pyenv local 3.11.7 in a project directory. Once the desired version is active, create a venv with python -m venv .venv and it will use that version. Conda also manages multiple Python versions within its environments.
Conclusion
Virtual environments are not optional in Python development — they are a fundamental practice that every project should use from day one. The barrier to entry is zero: python -m venv .venv && source .venv/bin/activate takes five seconds and immediately gives you isolated dependency management.
For beginners, start with venv and pip. Once comfortable, graduate to Poetry for its superior dependency resolution and lock file support. If you work in data science, embrace conda for managing complex native dependencies. And if you maintain projects across multiple Python versions, pyenv is indispensable.
The most important takeaway: never install project dependencies globally. Every project gets its own environment. Every environment is documented in a requirements or lock file. Every environment is disposable and reproducible. Follow these principles and you will avoid the dependency conflicts that plague developers who skip this step.
Related Resources
- Python Pandas: The Complete Data Analysis Guide — master data analysis after setting up your Python environment
- Bash Scripting: The Complete Guide — automate environment setup and deployment scripts
- Python for Beginners: The Complete Guide — learn Python fundamentals before diving into project management
- TOML: The Complete Guide — understand the configuration format used by pyproject.toml