Python Virtual Environment: How to Create One with venv and pip
A virtual environment gives each project its own packages. Learn to create one with venv, install with pip, pin dependencies, and fix the common errors.
Two projects live on one laptop. The first needs requests 2.20 because an old internal library pins it. The second needs requests 2.27. You run pip install for the second project, and the first one stops working. Nothing warned you, because one Python installation holds only one version of each package.
A Python virtual environment solves this. It is a directory that contains a link to a Python interpreter and a private site-packages folder. Packages you install there stay there, so each project gets the versions it needs.
This guide uses Python 3.10 and pip 22.x on macOS, Linux, and Windows. You need a terminal and a working Python install. The venv module ships with Python, so you do not need to install anything first. If you are still deciding whether Python suits your project, read about where Python fits and where it does not.
My position: start with venv and pip, and move to a heavier tool only when you can name the problem it solves for you. Most projects never need more.
What a Python virtual environment actually is
A virtual environment is not a copy of Python and it is not a sandbox. It is a small folder that tells an existing interpreter to look for packages in a different place. PEP 405 defines the mechanism.
project/
app.py
requirements.txt
.venv/
pyvenv.cfg marks this folder as a virtual environment
bin/ (Scripts\ on Windows)
python link to the base interpreter
pip
activate shell script that edits PATH
lib/python3.10/site-packages/
requests/ packages installed for this project only
The key file is pyvenv.cfg. When Python starts, it looks for that file next to its own executable. If the file exists, Python sets sys.prefix to the environment folder and loads packages from there.
home = /usr/local/bin
include-system-site-packages = false
version = 3.10.2
Consequently, a virtual environment costs a few megabytes and takes seconds to build. The trade-off is that it depends on the base interpreter. If you uninstall or upgrade that Python, the environment breaks and you must rebuild it.
Create a Python virtual environment with venv
First, confirm which interpreter you are about to use. The environment inherits its Python version from the command that creates it.
python --version
Python 3.10.2
On macOS and Linux, the command is often python3. On Windows, you can use the launcher: py -3.10. Next, move into your project folder and create the environment.
mkdir weather-cli
cd weather-cli
python -m venv .venv
The command prints nothing when it succeeds. It creates a .venv folder and installs pip into it. The name .venv is a convention, and editors such as VS Code detect it automatically.
Activate the environment
Activation changes your shell so that python and pip point at the environment. The command differs by operating system and shell.
| Platform and shell | Activate command |
|---|---|
| macOS or Linux, bash or zsh | source .venv/bin/activate |
| macOS or Linux, fish | source .venv/bin/activate.fish |
| Windows, PowerShell | .venv\Scripts\Activate.ps1 |
| Windows, Command Prompt | .venv\Scripts\activate.bat |
After activation, your prompt shows the environment name:
(.venv) $
To leave the environment, run deactivate. This restores your shell and deletes nothing.
Verify that it worked
Do not trust the prompt alone. Instead, ask Python where it is running from. Save this file as check_env.py.
import sys
def in_virtual_environment() -> bool:
return sys.prefix != sys.base_prefix
def main() -> None:
print(f'interpreter: {sys.executable}')
print(f'prefix: {sys.prefix}')
print(f'base prefix: {sys.base_prefix}')
print(f'virtual env: {in_virtual_environment()}')
if __name__ == '__main__':
main()
python check_env.py
interpreter: /home/dev/weather-cli/.venv/bin/python
prefix: /home/dev/weather-cli/.venv
base prefix: /usr/local
virtual env: True
Inside an environment, sys.prefix points at the environment while sys.base_prefix points at the real installation. When the two match, you are using the base interpreter.
Install packages with pip
With the environment active, install packages using python -m pip. This form runs the pip that belongs to the interpreter you named. By contrast, a bare pip command runs whichever pip appears first on your PATH, which may belong to another Python.
python -m pip install --upgrade pip
python -m pip install requests
Successfully installed certifi-2021.10.8 charset-normalizer-2.0.11 idna-3.3 requests-2.27.1 urllib3-1.26.8
You asked for one package and received five. The other four are transitive dependencies: packages that requests itself needs. This detail matters when you record your dependencies.
Now use the package. Save this as fetch.py.
import sys
import requests
def fetch_status(url: str) -> int:
response = requests.get(url, timeout=10)
return response.status_code
def main() -> int:
url = sys.argv[1] if len(sys.argv) > 1 else 'https://example.com'
try:
status = fetch_status(url)
except requests.RequestException as error:
print(f'request failed: {error}')
return 1
print(f'{url} returned {status}')
return 0
if __name__ == '__main__':
raise SystemExit(main())
python fetch.py
https://example.com returned 200
If you deactivate the environment and run the script with the base interpreter, you get ModuleNotFoundError: No module named 'requests'. That error is the isolation working as designed.
Record dependencies in requirements.txt
A virtual environment lives on your machine only. Therefore, you need a text file that lets anyone rebuild it. The simplest approach is pip freeze.
python -m pip freeze > requirements.txt
certifi==2021.10.8
charset-normalizer==2.0.11
idna==3.3
requests==2.27.1
urllib3==1.26.8
A teammate then rebuilds the same environment with three commands:
python -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
Finally, keep the environment itself out of Git. Add one line to .gitignore:
.venv/
Wrong and right: unpinned versus pinned
The most common mistake is a requirements file without versions.
# WRONG: installs whatever is newest on the day you run it
requests
# RIGHT: installs the version you tested
requests==2.27.1
The unpinned file works today and fails on some later day, when a new major release changes behavior. The pinned file gives the same result every time. However, pins go stale, so you must upgrade them on purpose and rerun your tests.
Separate what you want from what you got
A frozen file mixes your direct dependencies with transitive ones. Six months later, nobody remembers whether the project needs idna or merely received it. A two-file pattern fixes this. List only direct dependencies in requirements.in:
requests>=2.27,<3
Then generate the fully pinned requirements.txt from it. The pip-tools package automates the step with its pip-compile command:
python -m pip install pip-tools
pip-compile requirements.in
The output file pins every package and notes which direct dependency pulled it in. The trade-off is one more tool and one more file to maintain.
The myth: you must activate the environment
Many tutorials say a virtual environment only works after activation. That is false. Activation is a convenience that puts the environment’s bin folder first on your PATH. The isolation comes from pyvenv.cfg, not from the shell.
Consequently, you can call the environment’s interpreter by its full path and get the same isolation:
.venv/bin/python fetch.py
On Windows, the path is .venv\Scripts\python.exe. You can prove the point with the check script:
.venv/bin/python check_env.py
It prints virtual env: True in a shell that was never activated.
Here is the insight that follows: treat activation as something for humans and the full path as something for machines. Cron jobs, systemd units, and CI steps do not run your shell profile, so an activate line in them is fragile. A direct path to .venv/bin/python has no hidden state and fails loudly if the environment is missing.
venv compared with other tools
Several tools create or manage environments. They overlap, which confuses newcomers. This table shows what each one adds.
| Tool | Ships with Python | What it adds | Choose it when |
|---|---|---|---|
venv |
Yes | Creates environments, nothing else | You want the default with no installs |
virtualenv |
No | Faster creation, more options, older Python support | You build many environments, for example in CI |
pipx |
No | Installs each command-line tool in its own environment | You install tools such as black for global use |
| pipenv | No | Environment plus Pipfile and a lock file |
You want one command for both jobs in an application |
| Poetry 1.1 | No | Environment, lock file, and package publishing | You build a library or want strict locking |
| conda | No | Manages Python itself and non-Python libraries | You need compiled scientific stacks or several Python versions |
Note that venv does not install Python versions. If one project needs Python 3.9 and another needs 3.10, install both interpreters first, then create each environment with the matching one, for example python3.9 -m venv .venv.
Fix the errors beginners hit
A handful of errors account for most failed first attempts. Each has a short fix.
ensurepip is not available
On Debian and Ubuntu, python3 -m venv .venv can fail with this message:
The virtual environment was not created successfully because ensurepip is not
available. On Debian/Ubuntu systems, you need to install the python3-venv
package using the following command.
These distributions split venv into a separate system package. Install it, delete the half-built folder, and try again:
sudo apt install python3.10-venv
rm -rf .venv
python3 -m venv .venv
Running scripts is disabled on this system
PowerShell blocks the activation script by default on many Windows machines. The error says that Activate.ps1 cannot be loaded because running scripts is disabled. Allow locally created scripts for your user account:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Alternatively, skip activation and call .venv\Scripts\python.exe directly.
The environment broke after moving the folder
Scripts inside .venv/bin contain the absolute path of the environment. If you rename or move the project, commands such as pip fail with a bad interpreter error. Do not repair it by hand. Delete .venv and rebuild it from requirements.txt, which takes under a minute.
pip installed the package but Python cannot import it
This happens when pip and python belong to different installations. Check both:
python -m pip --version
pip 22.0.2 from /home/dev/weather-cli/.venv/lib/python3.10/site-packages/pip (python 3.10)
The path in the output must sit inside your .venv folder. Using python -m pip every time prevents the mismatch.
How real systems use virtual environments
Production teams follow a few consistent patterns. They are worth copying from your first project.
- One environment per project, inside the project folder. A
.venvdirectory next to the code is easy to find, easy to delete, and recognized by editors. - Environments are disposable. Nobody backs one up or copies it between machines. Instead, the requirements file is the source of truth, and the folder gets rebuilt on demand.
- CI builds a fresh environment on every run. A clean build proves that
requirements.txtis complete. If a package exists only on a developer laptop, the build fails early. - Services run by full path. A systemd unit or cron entry calls
/srv/app/.venv/bin/pythondirectly, with no activation step. - Separate files for development tools. Teams keep test and lint tools in
requirements-dev.txtso that servers install only what the application needs.
A mistake I have seen in production is a deploy script that ran sudo pip install -r requirements.txt on a shared server. It upgraded a library that an operating system tool depended on, and the tool stopped working on the next run. Moving the service into its own environment under /srv fixed it, and it also made rollbacks a matter of rebuilding one folder.
Teams that deploy with containers often still create an environment inside the image, because it keeps application packages apart from the image’s system Python.
Choosing an environment tool: a decision framework
Answer these questions in order and stop at the first clear match.
- Do you need libraries that are not Python packages? If you need compiled scientific stacks such as specific CUDA or GDAL builds, use conda. It manages those binaries, and
pipdoes not. - Are you installing a command-line tool, not building a project? Use
pipx. It gives each tool its own environment and puts the command on your PATH. - Are you publishing a library to PyPI? Consider Poetry. It handles metadata, locking, and publishing in one workflow.
- Do you need a lock file with hashes for an application? Use
pip-toolson top ofvenv, or pipenv if you prefer a single command. - Otherwise? Use
venvandpipwith a pinnedrequirements.txt. It works everywhere Python does.
When NOT to use a virtual environment
A virtual environment is the right default, but it is the wrong tool in a few cases.
- Scripts that use only the standard library. A file that imports
json,pathlib, andcsvhas nothing to isolate. Run it with any Python 3.10 interpreter. - When you need a different Python version. An environment reuses the interpreter that created it. It cannot give you Python 3.9 if only 3.10 is installed. Install the other interpreter first, or use conda.
- When you need operating system isolation. An environment does not isolate system libraries, environment variables, or the file system. Code inside it can read and write anything your user can. For that level of separation, you need a container or a virtual machine.
Common mistakes
- Installing with sudo. Running
sudo pip installwrites into the system Python. It can break operating system tools that depend on specific package versions. - Committing the .venv folder. The folder holds absolute paths and platform-specific binaries. It bloats the repository and fails on every other machine.
- Forgetting to activate before installing. The package lands in the base interpreter, and your project then fails with
ModuleNotFoundErroron a clean machine. - Leaving versions unpinned. A file that lists only package names installs different versions over time. Builds that passed last month fail without any code change.
- Freezing a polluted environment. If you experimented with ten packages and then ran
pip freeze, all ten enter your requirements. Servers install packages the code never imports. - Moving or renaming the project folder. Scripts in the environment hold the old absolute path, so
pipstops working. Rebuild the environment after any move.
Key takeaways
- Create one environment per project with
python -m venv .venv. - Always install with
python -m pip, so the package goes to the interpreter you intend. - Verify isolation by comparing
sys.prefixwithsys.base_prefix. - Pin exact versions in
requirements.txt, and add.venv/to.gitignore. - Activation is optional: scripts, cron jobs, and services should call
.venv/bin/pythonby full path. - Treat environments as disposable, and rebuild them instead of repairing them.
- Reach for conda, Poetry, or pipenv only when you can name the problem that
venvandpipleave unsolved.
FAQ
How do I create a virtual environment in Python?
Run python -m venv .venv in your project folder. Then activate it with source .venv/bin/activate on macOS or Linux, or .venv\Scripts\Activate.ps1 in Windows PowerShell.
What is the difference between venv and virtualenv?
The venv module ships with Python and covers the basics. The virtualenv package is a separate install that creates environments faster and offers more options. For most projects, venv is enough.
Should I commit my virtual environment to Git?
No. Add .venv/ to .gitignore and commit requirements.txt instead. The environment contains absolute paths and compiled files that work only on the machine that built it.
How do I delete a virtual environment?
Run deactivate if it is active, then delete the folder, for example with rm -rf .venv. Nothing else on your system refers to it, so no further cleanup is needed.
Do I need a virtual environment for every project?
Yes, for any project that installs third-party packages. A script that uses only the standard library does not need one.
One project, one environment, one requirements file
A virtual environment takes seconds to create and removes a whole class of version conflicts. Keep it inside the project, keep it out of Git, and keep a pinned requirements file beside it. When something goes wrong, delete the folder and rebuild.
Rule of thumb: if you cannot rebuild your environment from a text file in one minute, you do not have an environment, you have a liability.
