Python virtual environments solve the problem of conflicting package versions across projects by giving each application its own isolated Python installation and package tree. On RHEL 8, the venv module is included with every Python 3 installation, so no additional packages are required to get started. This tutorial covers creating and activating virtual environments, managing packages within them, producing and consuming requirements.txt files, using virtualenvwrapper for convenience when juggling multiple projects, and running a virtual environment from a systemd service.

Prerequisites

  • RHEL 8 with Python 3.8, 3.9, or 3.11 installed (see the previous tutorial)
  • Root or sudo access for systemd service steps
  • pip3 installed and up to date

Step 1 — Create a Virtual Environment

Choose a directory for your project and create the virtual environment inside it. The --upgrade-deps flag ensures pip and setuptools inside the venv start at the latest version.

mkdir -p /opt/myapp && cd /opt/myapp
python3 -m venv myenv --upgrade-deps
ls myenv/bin/

The myenv/ directory contains its own Python interpreter, pip, and an isolated site-packages tree. Nothing installed inside this venv affects the system Python.

Step 2 — Activate and Use the Virtual Environment

Activate the venv to prepend its bin/ directory to your PATH. After activation, the bare python and pip commands resolve to the venv’s copies, not the system ones.

source myenv/bin/activate
which python
python --version
pip install requests flask gunicorn
pip list

Your shell prompt changes to show (myenv) as a reminder that the venv is active. To leave the venv, run deactivate.

Step 3 — Manage Dependencies with requirements.txt

A requirements.txt file records the exact package versions in a venv so the environment can be recreated identically on another machine or in CI/CD.

pip freeze > requirements.txt
cat requirements.txt
# On a new system or fresh venv:
pip install -r requirements.txt

Commit requirements.txt to version control. For production deployments, pin every version with == as pip freeze produces, rather than using loose specifiers like >=.

Step 4 — Deactivate and Delete a Virtual Environment

Deactivating a venv returns your shell to the system Python. Because a venv is a plain directory, deleting it is all that is needed to remove it entirely.

deactivate
which python3
rm -rf /opt/myapp/myenv
# Recreate from requirements.txt:
python3 -m venv myenv --upgrade-deps
source myenv/bin/activate
pip install -r requirements.txt

Step 5 — Use virtualenvwrapper for Multiple Projects

virtualenvwrapper adds mkvirtualenv, workon, and rmvirtualenv commands that store all venvs in a central location (~/.virtualenvs by default), which is cleaner than having venv directories scattered across project folders.

pip3 install --user virtualenvwrapper
echo 'export WORKON_HOME=$HOME/.virtualenvs' >> ~/.bashrc
echo 'export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3' >> ~/.bashrc
echo 'source ~/.local/bin/virtualenvwrapper.sh' >> ~/.bashrc
source ~/.bashrc
mkvirtualenv myproject
workon myproject
pip install django
deactivate
rmvirtualenv myproject

Step 6 — Run a Virtual Environment from a systemd Service

When deploying a Python application as a daemon, point the systemd ExecStart directly to the venv’s Python interpreter rather than activating the venv in a shell wrapper. This is simpler and avoids sourcing shell scripts inside service units.

cat > /etc/systemd/system/myapp.service << 'EOF'
[Unit]
Description=My Python Application
After=network.target

[Service]
Type=simple
User=myappuser
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/myenv/bin/python /opt/myapp/app.py
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now myapp
systemctl status myapp

Conclusion

You have created and activated Python virtual environments on RHEL 8 using the built-in venv module, managed packages inside them, captured and reproduced dependencies with requirements.txt, and learned how to use virtualenvwrapper for managing multiple environments. The systemd service example demonstrated the production-ready pattern of referencing the venv interpreter directly, which is cleaner than activation scripts. Isolated environments are a fundamental best practice for every Python project regardless of size.

Next steps: How to Deploy a Django Application with Gunicorn and Nginx on RHEL 8, How to Install Python 3 and pip on RHEL 8, and How to Run a Python Flask Application with uWSGI on RHEL 8.