Managing Multiple Python Versions with Pyenv on Ubuntu

Ubuntu includes Python for system utilities, desktop components, and development work, but the version supplied by the distribution is not always the one an application requires. A project may depend on Python 3.10 while another needs Python 3.12, and upgrading the system interpreter to satisfy one project can disrupt tools managed by Ubuntu itself.

Pyenv provides a practical way to install and switch between several Python versions without replacing Ubuntu’s own Python. It works well for developers, students, data analysts, and anyone maintaining projects with different requirements. Once configured, each project can use its own interpreter while the operating system remains stable.

Why Separate Python Versions Matter

Python applications often depend on a particular interpreter version as well as specific packages. A web application tested with Python 3.11 may fail under Python 3.13 if a dependency has not yet been updated. Older automation scripts can have the opposite problem: they may rely on behaviour or standard-library modules that have changed in newer releases.

Ubuntu’s system Python should be treated as part of the operating system. Commands such as apt, desktop utilities, and administrative scripts may expect the distribution’s chosen interpreter. Changing /usr/bin/python3, removing Ubuntu packages, or installing a replacement over the system copy can create difficult-to-diagnose problems after an update.

Pyenv avoids that risk by placing user-managed Python builds in your home directory. You can select a version for your shell or a single project, then return to Ubuntu’s default interpreter whenever necessary. This is useful whether your development machine is in Melbourne, Perth, or a home office elsewhere in Australia.

Preparing Ubuntu For Pyenv

Pyenv compiles most Python versions from source, so Ubuntu needs a compiler, development headers, and several libraries. Open Terminal and install the common build requirements:

sudo apt update
sudo apt install -y build-essential curl git \
  libssl-dev zlib1g-dev libbz2-dev libreadline-dev \
  libsqlite3-dev libncursesw5-dev xz-utils tk-dev \
  libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev

These packages support encryption, compressed files, SQLite, terminal interfaces, and other features commonly enabled in a Python build. The exact package names can vary slightly between Ubuntu releases, but the command is suitable for current long-term support versions such as Ubuntu 22.04 and 24.04.

A compiler build can take several minutes and may use considerable processor resources. On a modest laptop, it is normal for installation to be slower than downloading a package. If you are using a metered connection in regional Australia, the initial source downloads are still relatively small, but keeping build dependencies installed makes future Python installations easier.

Installing Pyenv In Your User Account

The standard installer downloads pyenv into ~/.pyenv and adds the required shell configuration. Run it as your normal user rather than with sudo:

curl https://pyenv.run | bash

The installer prints configuration lines that need to be added to your shell startup file. For Bash, edit ~/.bashrc:

nano ~/.bashrc

Add these lines near the end of the file:

export PYENV_ROOT="$HOME/.pyenv"
[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init - bash)"

Save the file, close the editor, and reload the configuration:

source ~/.bashrc

Ubuntu users who have changed their default shell to Zsh should place the equivalent configuration in ~/.zshrc:

export PYENV_ROOT="$HOME/.pyenv"
[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init - zsh)"

Check that the command is available:

pyenv --version

If the shell cannot find pyenv, open a new Terminal window and check that the configuration was added to the correct startup file. A common cause is editing .bashrc while using Zsh, or opening a non-login shell with a different configuration path.

Finding And Installing Python Releases

Pyenv can list versions that are available to install:

pyenv install --list

The output includes many CPython releases along with alternative implementations. For most Ubuntu users, a current maintained CPython version is the sensible choice. For example, to install Python 3.12.8 when that release is available:

pyenv install 3.12.8

You can install another version alongside it:

pyenv install 3.11.11

The specific patch release changes over time, so use pyenv install --list to select a currently available version. Python patch releases include security fixes and bug corrections, making them preferable to an older patch version when project compatibility allows.

After installation, display the versions managed by pyenv:

pyenv versions

An asterisk marks the version currently active. If compilation fails, read the final lines of the output carefully. Missing development libraries, insufficient disk space, and interrupted downloads are more common causes than a problem with pyenv itself. Removing an incomplete build directory and trying again after installing the required package often resolves the issue.

Selecting A Version Globally Or Locally

Pyenv supports three useful levels of version selection. The global setting applies to your user account in new shells:

pyenv global 3.12.8
python --version

A local setting applies to the current directory and its subdirectories. Move into a project folder and run:

cd ~/projects/inventory-tool
pyenv local 3.11.11

This creates a .python-version file containing the selected release. Whenever you enter that directory, pyenv automatically switches to the matching interpreter. Leaving the directory returns the shell to the global selection.

For a temporary choice in the current shell, use:

pyenv shell 3.12.8

That setting disappears when the shell closes. This is convenient for quickly testing a package against a different interpreter without changing project files or your usual environment. Run pyenv version to see which release is active and where the selection came from.

Creating Isolated Project Environments

A separate Python version does not automatically isolate installed packages. For each project, create a virtual environment after selecting its interpreter:

cd ~/projects/inventory-tool
pyenv local 3.11.11
python -m venv .venv
source .venv/bin/activate

The terminal prompt usually changes to show .venv. Package commands now operate inside the project environment:

python -m pip install --upgrade pip
python -m pip install requests

Using python -m pip makes sure that pip belongs to the active interpreter. You can record the project’s dependencies with:

python -m pip freeze > requirements.txt

To leave the environment, run:

deactivate

The .venv directory should generally be excluded from Git because it contains installed files specific to one machine. Add .venv/ to .gitignore, then let other users create their own environment from the requirements file. This workflow suits local development, university coursework, and small business projects where repeatable setup matters.

For projects that require frequent switching, the optional pyenv-virtualenv plugin can combine version selection and environment naming. However, Python’s built-in venv module is easier to understand and is sufficient for most Ubuntu users.

Keeping Pyenv And Python Environments Healthy

Pyenv itself is updated through its Git repository when installed by the standard script. From the pyenv directory, update it with:

cd ~/.pyenv
git pull

Before installing a newer Python release, update Ubuntu’s package information and keep the build tools current:

sudo apt update
sudo apt upgrade

Do not remove an older interpreter simply because a newer one is available. Existing projects may still use it, and the disk space saved is usually modest. When a version is no longer needed, confirm that no project points to it before removing it:

pyenv uninstall 3.11.11

The command removes only the pyenv-managed build, not Ubuntu’s system Python.

When troubleshooting, begin with a few checks:

pyenv version
pyenv which python
python -c "import sys; print(sys.executable)"

These commands reveal the selected release and the actual executable path. If python points to /usr/bin/python3, pyenv may not be initialised in the current shell. If a project unexpectedly uses another version, inspect .python-version in the current directory and its parent directories.

Using Pyenv Alongside Ubuntu Tools

A pyenv-managed Python is ideal for application development, scripts, testing, and virtual environments. Ubuntu’s apt remains the right tool for system packages and commands used by the operating system. Avoid using sudo pip install, since it can mix files from pip with files managed by apt and make future maintenance harder.

Editors and integrated development environments may need to be told which interpreter to use. In Visual Studio Code, select the interpreter inside the project’s .venv directory. For a project using Python 3.11, the path will commonly look like:

/home/your-name/projects/inventory-tool/.venv/bin/python

A terminal opened by an editor may not load the same shell startup files as a regular Ubuntu Terminal. If the editor cannot see pyenv versions, launch it from a correctly configured shell or select the virtual environment directly.

The same care applies to scheduled jobs and services. Cron, systemd, and desktop launchers may not load .bashrc, so they should use an explicit path to the virtual environment’s Python executable. This prevents a script from silently running with the wrong interpreter after a version change.

In Australia, developers may also encounter Python tools supplied through a company-managed laptop, university lab, or cloud workstation. Local IT policies can restrict compilation or shell changes, while Australian hosting providers may offer a preconfigured Ubuntu image. In those settings, keep project environments in your home directory and check organisational rules before installing build tools.

A reliable setup leaves /usr/bin/python3 untouched, records each project’s required Python version, and gives every project its own virtual environment. Start by installing the build dependencies, run pyenv’s user-level installer, and set a local version with pyenv local inside one test project.