Installing Major Python Versions on Debian Trixie

Very, very often you have to deal with the need to use a certain version of Python, for example, for torch, or for something specific.
Using conda or Docker with nvidia-container-toolkit and then setting up images like nvidia/cuda:13.0.1-runtime-ubuntu22.04 feels counterintuitive to me, even though I’ve done it many times.
And an additional packages have to be installed in the container.

It’s easier and more convenient to compile everything on the host system, but at the same time prevent contamination of the entire system with third-party libraries and executable files.

The following is an example of building multiple Python versions for the verpy group only, which is a bit more convenient than installing for just one user.

  • ai - an arbitrary user from whom the compilation is performed.
  • verpy - the group that will be allowed to use these versions.
1
2
3
4
apt install \
    build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev \
    wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev \
    liblzma-dev libgdbm-dev libnsl-dev libgdbm-compat-dev

mkdir /opt/openssl
mkdir /opt/python
groupadd --gid 42398 verpy
usermod -aG verpy ai

Provide write access for the installation

chown ai:verpy /opt/openssl /opt/python
chmod u=rwX,g=rX,o-rwXx /opt/openssl /opt/python

Switch to the user from whom the build and installation will be performed

su ai
mkdir -p $HOME/src/openssl
cd $HOME/src/openssl

Installing the required versions of openssl


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# 1.0.2u > python 3.7
wget https://github.com/openssl/openssl/releases/download/OpenSSL_1_0_2u/openssl-1.0.2u.tar.gz
wget https://github.com/openssl/openssl/releases/download/OpenSSL_1_0_2u/openssl-1.0.2u.tar.gz.asc
wget https://github.com/openssl/openssl/releases/download/OpenSSL_1_0_2u/openssl-1.0.2u.tar.gz.sha256
gpg --keyserver keys.gnupg.net --recv-keys 8657ABB260F056B1E5190839D9C4D26D0E604491
gpg --verify openssl-1.0.2u.tar.gz.asc
tar zxf openssl-1.0.2u.tar.gz && cd openssl-1.0.2u
./config --prefix=/opt/openssl/1.0.2u --openssldir=/opt/openssl/1.0.2u/ssl
make -j8
make install_sw

# exec bash - reinitializes and resets any environment variables that may remain from a previous installation
exec bash
cd $HOME/src/openssl

# 1.1.1w > python 3.9
wget https://github.com/openssl/openssl/releases/download/OpenSSL_1_1_1w/openssl-1.1.1w.tar.gz
wget https://github.com/openssl/openssl/releases/download/OpenSSL_1_1_1w/openssl-1.1.1w.tar.gz.asc
wget https://github.com/openssl/openssl/releases/download/OpenSSL_1_1_1w/openssl-1.1.1w.tar.gz.sha256
gpg --keyserver keys.gnupg.net --recv-keys EFC0A467D613CB83C7ED6D30D894E2CE8B3D79F5
gpg --verify openssl-1.1.1w.tar.gz.asc
tar zxf openssl-1.1.1w.tar.gz && cd openssl-1.1.1w
./config --prefix=/opt/openssl/1.1.1w --openssldir=/opt/openssl/1.1.1w/ssl
make -j8
make install_sw

exec bash
cd $HOME/src/openssl

# 3.0.17 > python 3.10 ; 3.11
wget https://github.com/openssl/openssl/releases/download/openssl-3.0.17/openssl-3.0.17.tar.gz
wget https://github.com/openssl/openssl/releases/download/openssl-3.0.17/openssl-3.0.17.tar.gz.asc
wget https://github.com/openssl/openssl/releases/download/openssl-3.0.17/openssl-3.0.17.tar.gz.sha256
gpg --keyserver keys.gnupg.net --recv-keys BA5473A2B0587B07FB27CF2D216094DFD0CB81EF
gpg --verify openssl-3.0.17.tar.gz.asc
tar zxf openssl-3.0.17.tar.gz && cd openssl-3.0.17
./config --prefix=/opt/openssl/3.0.17 --openssldir=/opt/openssl/3.0.17/ssl
make -j8
make install_sw

exec bash
cd $HOME/src/openssl

# 3.5.4 > python 3.12
wget https://github.com/openssl/openssl/releases/download/openssl-3.5.4/openssl-3.5.4.tar.gz
wget https://github.com/openssl/openssl/releases/download/openssl-3.5.4/openssl-3.5.4.tar.gz.asc
wget https://github.com/openssl/openssl/releases/download/openssl-3.5.4/openssl-3.5.4.tar.gz.sha256
gpg --keyserver keys.gnupg.net --recv-keys BA5473A2B0587B07FB27CF2D216094DFD0CB81EF
gpg --verify openssl-3.5.4.tar.gz.asc
tar zxf openssl-3.5.4.tar.gz && cd openssl-3.5.4
./config --prefix=/opt/openssl/3.5.4 --openssldir=/opt/openssl/3.5.4/ssl
make -j8
make install_sw

exec bash
cd $HOME/src

Installing Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
mkdir -p $HOME/src/python
cd $HOME/src/python

# python 3.7.17

wget https://www.python.org/ftp/python/3.7.17/Python-3.7.17.tar.xz
wget https://www.python.org/ftp/python/3.7.17/Python-3.7.17.tar.xz.asc
gpg --keyserver keys.gnupg.net --search-keys 0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D
gpg --verify Python-3.7.17.tar.xz.asc
tar xvf Python-3.7.17.tar.xz && cd Python-3.7.17
LDFLAGS="${LDFLAGS} -Wl,-rpath=/opt/openssl/1.1.1w/lib" ./configure --prefix=/opt/python/3.7.17 --enable-optimizations --with-openssl=/opt/openssl/1.1.1w --with-lto --with-computed-gotos --with-system-ffi
make -j8
make test
make install

exec bash
cd $HOME/src/python

# python 3.9.19

wget https://www.python.org/ftp/python/3.9.19/Python-3.9.19.tar.xz
wget https://www.python.org/ftp/python/3.9.19/Python-3.9.19.tar.xz.asc
gpg --keyserver keys.gnupg.net --search-keys E3FF2839C048B25C084DEBE9B26995E310250568
gpg --verify Python-3.9.19.tar.xz.asc
tar xvf Python-3.9.19.tar.xz && cd Python-3.9.19
LDFLAGS="${LDFLAGS} -Wl,-rpath=/opt/openssl/1.1.1w/lib" ./configure --prefix=/opt/python/3.9.19 --enable-optimizations --with-openssl=/opt/openssl/1.1.1w --with-lto --with-computed-gotos --with-system-ffi
make -j8
make test
make install

exec bash
cd $HOME/src/python

# python 3.9.25

wget https://www.python.org/ftp/python/3.9.25/Python-3.9.25.tar.xz
wget https://www.python.org/ftp/python/3.9.25/Python-3.9.25.tar.xz.asc
gpg --keyserver keys.gnupg.net --search-keys E3FF2839C048B25C084DEBE9B26995E310250568
gpg --verify Python-3.9.25.tar.xz.asc
tar xvf Python-3.9.25.tar.xz && cd Python-3.9.25
LDFLAGS="${LDFLAGS} -Wl,-rpath=/opt/openssl/1.1.1w/lib" ./configure --prefix=/opt/python/3.9.25 --enable-optimizations --with-openssl=/opt/openssl/1.1.1w --with-lto --with-computed-gotos --with-system-ffi
make -j8
make test
make install

exec bash
cd $HOME/src/python

# python 3.10.19

wget https://www.python.org/ftp/python/3.10.19/Python-3.10.19.tar.xz
wget https://www.python.org/ftp/python/3.10.19/Python-3.10.19.tar.xz.asc
gpg --keyserver keys.gnupg.net --search-keys CFDCA245B1043CF2A5F97865FFE87404168BD847
gpg --verify Python-3.10.19.tar.xz.asc
tar xvf Python-3.10.19.tar.xz && cd Python-3.10.19
LDFLAGS="${LDFLAGS} -Wl,-rpath=/opt/openssl/3.0.17/lib" ./configure --prefix=/opt/python/3.10.19 --enable-optimizations --with-openssl=/opt/openssl/3.0.17 --with-lto --with-computed-gotos --with-system-ffi
make -j8
make test
make install

exec bash
cd $HOME/src/python

# python 3.11.14

wget https://www.python.org/ftp/python/3.11.14/Python-3.11.14.tar.xz
wget https://www.python.org/ftp/python/3.11.14/Python-3.11.14.tar.xz.asc
gpg --keyserver keys.gnupg.net --search-keys CFDCA245B1043CF2A5F97865FFE87404168BD847
gpg --verify Python-3.11.14.tar.xz.asc
tar xvf Python-3.11.14.tar.xz && cd Python-3.11.14
LDFLAGS="${LDFLAGS} -Wl,-rpath=/opt/openssl/3.0.17/lib" ./configure --prefix=/opt/python/3.11.14 --enable-optimizations --with-openssl=/opt/openssl/3.0.17 --with-lto --with-computed-gotos --with-system-ffi
make -j8
make test
make install

exec bash
cd $HOME/src/python

# python 3.12.12

wget https://www.python.org/ftp/python/3.12.12/Python-3.12.12.tar.xz
wget https://www.python.org/ftp/python/3.12.12/Python-3.12.12.tar.xz.asc
gpg --keyserver keys.gnupg.net --search-keys 7169605F62C751356D054A26A821E680E5FA6305
gpg --verify Python-3.12.12.tar.xz.asc
tar xvf Python-3.12.12.tar.xz && cd Python-3.12.12
LDFLAGS="${LDFLAGS} -Wl,-rpath=/opt/openssl/3.5.4/lib" ./configure --prefix=/opt/python/3.12.12 --enable-optimizations --with-openssl=/opt/openssl/3.5.4 --with-lto --with-computed-gotos
make -j8
make test
make install

exec bash

strip - it’s up to you

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# [ --strip-all | --strip-unneeded ]
strip --strip-unneeded /opt/python/3.10.19/bin/python3.10
strip --strip-unneeded /opt/python/3.11.14/bin/python3.11
strip --strip-unneeded /opt/python/3.12.12/bin/python3.12
strip --strip-unneeded /opt/python/3.7.17/bin/python3.7
strip --strip-unneeded /opt/python/3.7.17/bin/python3.7m
strip --strip-unneeded /opt/python/3.9.19/bin/python3.9
strip --strip-unneeded /opt/python/3.9.25/bin/python3.9
strip --strip-unneeded /opt/openssl/1.0.2u/bin/openssl
strip --strip-unneeded /opt/openssl/1.1.1w/bin/openssl
strip --strip-unneeded /opt/openssl/3.0.17/bin/openssl
strip --strip-unneeded /opt/openssl/3.5.4/bin/openssl

exit

Now let’s take away the write access

That’s it, OpenSSL and Python are installed, write access may must be removed.
Read, write, and execute permissions only for the verpy group.
All other users will not interact with these packages in any way, this is not FrankenDebian.

chown -R nobody:verpy /opt/openssl /opt/python
chmod -R u=r,g=rXx,o-rwXx /opt/openssl /opt/python

Linking all versions of Python


Run the following script as the ai user:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/bash

if [[ ! -d "$HOME/.local/bin" ]]; then
    mkdir -p $HOME/.local/bin
fi

declare -A dirs=(
  ["3.7.17"]="2to3-3.7 easy_install-3.7 idle3.7 pip3.7 pydoc3.7 python3.7 python3.7m pyvenv-3.7 python3.7m-config"
  ["3.9.25"]="2to3-3.9 idle3.9 pip3.9 pydoc3.9 python3.9 python3.9-config"
  ["3.10.19"]="2to3-3.10 idle3.10 pip3.10 pydoc3.10 python3.10 python3.10-config"
  ["3.11.14"]="2to3-3.11 idle3.11 pip3.11 pydoc3.11 python3.11 python3.11-config"
  ["3.12.12"]="2to3-3.12 idle3.12 pip3.12 pydoc3.12 python3.12 python3.12-config"
)

for dir in "${!dirs[@]}"; do
    printf '%s\n' "$dir"
    bins="${dirs[$dir]}"
    for bin in ${bins}; do
        printf '/opt/python/%s/bin/%s > ~/.local/bin/%s\n' "${dir}" "$bin" "$bin"
        ln -sf /opt/python/${dir}/bin/${bin} $HOME/.local/bin/${bin}
    done
    bins=''
done

Some tests

import ssl
ssl.OPENSSL_VERSION
import sys
print(sys.executable)

python3.7

1
2
3
4
5
6
7
8
9
Python 3.7.17 (default, Dec 22 2025, 10:58:22) 
[GCC 14.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import ssl
>>> ssl.OPENSSL_VERSION
'OpenSSL 1.1.1w  11 Sep 2023'
>>> import sys
>>> print(sys.executable)
/home/ai/.local/bin/python3.7

python3.12

1
2
3
4
5
Python 3.12.12 (main, Dec 22 2025, 12:33:21) [GCC 14.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import ssl
>>> ssl.OPENSSL_VERSION
'OpenSSL 3.5.4 30 Sep 2025'

Install torch with python 3.10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mkdir ~/.torch_env/
python3.10 -m venv ~/.torch_env/
source ~/.torch_env/bin/activate
which pip3
# /home/ai/.torch_env/bin/pip3
which python3.10
# /home/ai/.torch_env/bin/python3.10

python3.10 -m pip install --upgrade pip
pip3 install torch numpy

python3.10
from torch import cuda
print(cuda.get_device_name(cuda.current_device()))
1
2
3
4
5
6
python3.10
Python 3.10.19 (main, Dec 22 2025, 11:39:18) [GCC 14.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from torch import cuda
>>> print(cuda.get_device_name(cuda.current_device()))
NVIDIA RTX A3000 Laptop GPU

If some tests fail

You can continue the installation and then pass the same test under the tested version of Python:

1
2
1 test failed:
    test_urllib2

cd ~/src/python/Python-3.7.17/Lib

1
2
3
/opt/python/3.7.17/bin/python3.7 -m test test_ssl
/opt/python/3.7.17/bin/python3.7 -m test test_urllib2
== Tests result: SUCCESS ==