notice-icon

There are several subtle differences in managing versions of Python on older distributions of Ubuntu:

  • If you're running Ubuntu 18.04 or older, go here.
  • If you're running Ubuntu 20.04, go here.

Welcome to another edition of Managing Multiple Versions of Python on Ubuntu 2X.XX. Those paying attention may have noticed that this is the third time we've covered this topic. Why bother, you ask? Because I've recently had to do this myself, and the process for installing Python >3.12 on Ubuntu 24.04 is just different enough to make the Ubuntu 20.04 version of this post essentially useless. So, here we go again.

Whats the gist?

If you aren't aware by now, changing Ubuntu's packaged version of Python is a bad thing. Ubuntu itself leverages Python, so you can imagine how problematic it might be to change a dependency of your operating system. This is where Linux's built-in alternative install comes in handy.

alternative install is optimal for several reasons:

  1. We can install a new version of Python in parallel to the version of Python Ubuntu depends on so we don't ruin our machine.
  2. It's best to avoid messing around with your Python PATH whenever possible.
  3. We can easily switch the active version of Python on our machine via a convenient CLI.

We will walk through how to install the latest version of Python alongside Ubuntu's system Python versions safely and (relatively) easily.

Python via Deadsnakes

Deadsnakes PPA is an actively maintained repository of Python distributions available to Ubuntu. Deadsnakes carries the burden of hosting versions of Python that have been tried and tested to work on Ubuntu (their Github organization is essentially a collection of Ubuntu-friendly Python versions).

By adding the Deadsnakes PPA, we're making these Python versions visible to our Ubuntu machines:

sudo apt install software-properties-common
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt update

Add the deadsnakes repository

Upon adding this repository, you'll immediately receive a prompt explaining this verbosely. Press enter to move on.

Repository: 'Types: deb
URIs: https://ppa.launchpadcontent.net/deadsnakes/ppa/ubuntu/
Suites: noble
Components: main
'
Description:
This PPA contains more recent Python versions packaged for Ubuntu.

Disclaimer: there's no guarantee of timely updates in case of security problems or other issues. If you want to use them in a security-or-otherwise-critical environment (say, on a production server), you do so at your own risk.

Update Note
===========
Please use this repository instead of ppa:fkrull/deadsnakes.

Reporting Issues
================

Issues can be reported in the master issue tracker at:
https://github.com/deadsnakes/issues/issues

Supported Ubuntu and Python Versions
====================================

- Ubuntu 22.04 (jammy) Python3.7+ (NOT Python3.10)
- Ubuntu 24.04 (noble) Python3.7+ (NOT Python3.12)
- Ubuntu 26.04 (resolute) Python3.7+ (NOT Python3.14)
- Note: Python 3.10 (jammy), Python3.12 (noble), Python3.14 (resolute) are not provided by deadsnakes as upstream ubuntu provides those packages.

Why some packages aren't built:
- Note: for 22.04+, older python versions requre libssl<3 so they are not currently built
- If you need these, reach out to asottile to set up a private ppa

The packages may also work on other versions of Ubuntu or Debian, but that is not tested or supported.

Packages
========

The packages provided here are loosely based on the debian upstream packages with some modifications to make them more usable as non-default pythons and on ubuntu.  As such, the packages follow debian's patterns and often do not include a full python distribution with just `apt install python#.#`.  Here is a list of packages that may be useful along with the default install:

- `python#.#-dev`: includes development headers for building C extensions
- `python#.#-venv`: provides the standard library `venv` module
- `python#.#-distutils`: provides the standard library `distutils` module
- `python#.#-lib2to3`: provides the `2to3-#.#` utility as well as the standard library `lib2to3` module
- `python#.#-gdbm`: provides the standard library `dbm.gnu` module
- `python#.#-tk`: provides the standard library `tkinter` module

Third-Party Python Modules
==========================

Python modules in the official Ubuntu repositories are packaged to work with the Python interpreters from the official repositories. Accordingly, they generally won't work with the Python interpreters from this PPA. As an exception, pure-Python modules for Python 3 will work, but any compiled extension modules won't.

To install 3rd-party Python modules, you should use the common Python packaging tools.  For an introduction into the Python packaging ecosystem and its tools, refer to the Python Packaging User Guide:
https://packaging.python.org/installing/

Sources
=======
The package sources are available at:
https://github.com/deadsnakes/

Nightly Builds
==============

For nightly builds, see ppa:deadsnakes/nightly https://launchpad.net/~deadsnakes/+archive/ubuntu/nightly
More info: https://launchpad.net/~deadsnakes/+archive/ubuntu/ppa
Adding repository.
Press [ENTER] to continue or Ctrl-c to cancel.

Deadsnakes prompt explaining usage

To pick up the versions of Python that Deadsnakes makes visible to us, we still need to run a quick update:

sudo apt update

Update packages visible to Ubuntu

Now check to see if the version of Python you're looking for is available for download like so:

apt list | grep python3.14

Check for your desired version of Python (`python3.*`)

If available, you'll see an output like so:

apt list | grep python3.10

WARNING: apt does not have a stable CLI interface. Use with caution in scripts.

idle-python3.14/focal 3.14.6-1+focal1 all
libpython3.14-dbg/focal 3.14.6-1+focal1 amd64
libpython3.14-dev/focal 3.14.6-1+focal1 amd64
libpython3.14-minimal/focal 3.14.6-1+focal1 amd64
libpython3.14-stdlib/focal 3.14.6-1+focal1 amd64
libpython3.14-testsuite/focal 3.14.6-1+focal1 all
libpython3.14/focal 3.14.6-1+focal1 amd64
libqgispython3.14.4/focal 3.14.4+dfsg-1ubuntu2 amd64
python3.14-dbg/focal 3.14.6-1+focal1 amd64
python3.14-dev/focal 3.14.6-1+focal1 amd64
python3.14-distutils/focal 3.14.6-1+focal1 all
python3.14-examples/focal 3.14.6-1+focal1 all
python3.14-full/focal 3.14.6-1+focal1 amd64
python3.14-gdbm-dbg/focal 3.14.6-1+focal1 amd64
python3.14-gdbm/focal 3.14.6-1+focal1 amd64
python3.14-lib2to3/focal 3.14.6-1+focal1 all
python3.14-minimal/focal 3.14.6-1+focal1 amd64
python3.14-tk-dbg/focal 3.14.6-1+focal1 amd64
python3.14-tk/focal 3.14.6-1+focal1 amd64
python3.14-venv/focal 3.14.6-1+focal1 amd64
python3.14/focal 3.14.6-1+focal1 amd64

Confirm your version of Python exists for download

That's our green light! Go on and proceed to install your desired version of Python:

sudo apt install python3.14

Install Python3.x

Managing Alternative Python Installations

We now have two versions of Python installed on our machine: the system default (Python 3.12), and our newly added version (in my example, Python 3.14). We want to leave our system's default Python installation alone, but we want to run our apps written in Python 3.14... so how do we manage this?

Recognizing Multiple Versions of a Package via update-alternatives

update-alternatives is a native feature of Debian-based Linux distributions and conveniently handles these scenarios. update-alternatives is a CLI interface for installing and switching between alternative versions of packages. This applies to programming languages (like python), text editors (like vim), and really anything else apt can install.

We makeupdate-alternatives is aware of parallel versions via the --install flag, which accepts three parameters:

  1. The file path to the default system location of the package we're adding alternatives for. On Ubuntu 24.04, the default location for Python3 is /usr/bin/python3.
  2. Name of the package we're adding alternatives for ( python3 ).
  3. File path to an existing or newly installed version of the package. We add the existing version of a package to give ourselves the option to fall back if necessary. Adding the newly installed version allows us to switch to an alternative (likely newer) version.
  4. An integer used to "prioritize" our list of managed versions for human-readability purposes (for example, it is a good idea to mark the existing default version as 1, and label subsequent versions incrementally).

Thus, using update-alternatives to recognize an installation of Python on your system would look as follows:

update-alternatives --install [/path/to/python] [python3] [Priority (optional]

Structure of adding an install via update-alternatives

Begin by adding the system's default version of Python to update-alternatives.

update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.12 1

Make the system version of Python visible to update-alternatives

Next, we'll add the path to our newly installed version of Python (if you're not sure where this is, try which python3.x):

update-alternatives --install /usr/bin/python3.14 python3 /usr/bin/python3.14 2

Set alternative version for Python3

Let's check our work.

Listing all available versions of Python

update-alternatives --list python3 should now list the alternative installations of python3 we've installed and added to update-alternatives:

update-alternatives --list python3
/usr/bin/python3.12
/usr/bin/python3.14

List installed versions of Python visible to update-alternatives

Swapping Between Versions

Now we can swap between versions of Python! Run the following --config command to change the active version of Python you'd like to use:

update-alternatives --config python3

Swapping between versions

You should be hit with a prompt like the one below. This will list all the versions of Python your system recognizes. Select the version of Python you'd like to use by providing the "selection" number to the prompt:

There are 2 choices for the alternative python3 (providing /usr/bin/python3).

  Selection    Path                 Priority   Status
------------------------------------------------------------
  0            /usr/bin/python3.14   2         auto mode
* 1            /usr/bin/python3.12   1         manual mode
  2            /usr/bin/python3.14   2         manual mode

Press <enter> to keep the current choice[*], or type selection number:

CLI for switching active Python version

Respond to the above prompt with the selection number representing the Python version you want to use.

We've done the "hard" part, but there's a bit of housekeeping to take care of.

Finishing Touches

Now that we've switched to a freshly installed version of Python, we must install a few Python-related Ubuntu packages to ensure Python runs properly. These include pip (obviously), python-apt (necessary for Debian Python), distutils (supposedly-deprecated-but-kinda-not, and venv (to manage Python virtual environments).

First, we need to reinstall python3-apt with our new version of Python active:

sudo apt remove --purge python3-apt
sudo apt autoclean
sudo apt install python3-apt

Reinstall python3-apt for your alternative Python installation

Next, we'll install python-dev and python-venv:

sudo apt-get install python3.14-dev python3.14-venv

Install -dev and -venv packages

Installing Pip

⚠️ Important: This last part had me all fucked up, and is related to ensuring pip exists for your newly installed version of Python. This process has nothing to do with get-pip.py, as it has in the past.

With your new version of Python active, install pip via the standard python3 package - not versioned (e.g. 3.x):

apt install python3-pip

Install pip for your new Python installation

Now make sure it worked:

python3 -m pip --version
pip 24.0 from /usr/lib/python3/dist-packages/pip (python 3.14)

Checking whether your activated version of Python has pip

You'll notice above that the version of pip is listed in the output (pip 24.0), as well as our targeted version of Python (python 3.14), so we know this isn't picking up our other Python's pip installation.

If you want to upgrade your version of pip moving forward you'll have to use the following command:

apt install --only-upgrade python3-pip

Upgrading pip to the latest version

I call this out explicitly because OGs will remember that pip could previously be updated via pip itself, as such:

python3 -m pip install --upgrade pip

The now wrong and outdated way to upgrade pip

If you attempt this on Ubuntu moving forward, you'll see the following error:

Requirement already satisfied: pip in /usr/lib/python3/dist-packages (24.0)
Collecting pip
  Using cached pip-26.1.2-py3-none-any.whl.metadata (4.6 kB)
Using cached pip-26.1.2-py3-none-any.whl (1.8 MB)
Installing collected packages: pip
  Attempting uninstall: pip
    Found existing installation: pip 24.0
ERROR: Cannot uninstall pip 24.0, RECORD file not found. Hint: The package was installed by debian.

Failure when upgrading pip via pip

I might be an edge case of insane people who not only keep pip updated, but actually include pip install --upgrade pip as a step in my Python project build process. Needless to say, I was shook when my projects refused to build on a newer version of Python, but the reason is right there in the error message:

Hint: The package was installed by debian.

Introducing a breaking change to how Python is managed on Ubuntu/Debian is one thing. Opting to throw a resulting error message for a nebulous change you introduced with "Hint:" makes you an asshole.

You Did It

As absurd as it sounds, successfully updating Python on Ubuntu is a legitimate accomplishment. I've witnessed software developers of all backgrounds struggle with dumb things like "setting up Python." Some tasks are unintuitive and lack any conventional patterns or logic. Updating Python on Ubuntu is one of those tasks.

You've managed to prevail, so congratulations are in order. If you're new to Python, please don't be discouraged by how weirdly complicated this was. The miserable journey we've just embarked on is no indication of what software development is like... or any related profession, for that matter. From here on out, it's sunshine, rainbows, and snakes 🐍.