Dev

Install TensorFlow on Ubuntu 26.04 / 24.04 with GPU and CPU

On Ubuntu 26.04, pip install tensorflow does not fail. It quietly installs a release candidate and a nightly Keras build, because the system Python is 3.14 and the current stable TensorFlow ships wheels for Python 3.10 to 3.13 only. The import works and nothing warns you that it’s pre-release code. On Ubuntu 24.04, with Python 3.12, the same command installs the stable release.

Original content from computingforgeeks.com - post 5308

This guide shows how to install TensorFlow on Ubuntu with pip, with uv and a managed Python 3.13, and with the official Docker images, then verifies it with a short training run and Jupyter. The GPU steps were checked against the TensorFlow and NVIDIA docs but not run on GPU hardware. Everything else was run on Ubuntu 26.04 (Python 3.14.4) and 24.04 (Python 3.12.3) with TensorFlow 2.21.0 in October 2026.

Prerequisites

TensorFlow’s Linux wheels are built for x86_64 CPUs with AVX, so a VM whose CPU type hides AVX cannot import it at all. On Proxmox that includes the old kvm64 type and the GUI default x86-64-v2-AES, since AVX only arrives with x86-64-v3. Set the CPU type to host or x86-64-v3 before you start. The rest is modest for CPU work:

  • Ubuntu 26.04 LTS or 24.04 LTS, x86_64, with sudo access
  • A CPU with AVX (check with grep -o -m1 -w avx /proc/cpuinfo)
  • 4 GB RAM to follow along; the MNIST run below peaked at 1.5 GB resident
  • About 3 GB free disk for a CPU install, 7 GB for tensorflow[and-cuda], 12 GB for the GPU Docker image
  • For GPU training: an NVIDIA card and driver 525.60.13 or newer

Our lab VMs had 2 vCPUs (Intel i7-6700) and 4 GB RAM. That is a floor for following along, not a sizing recommendation. Real training is bound by GPU memory, because the model, its activations and each batch have to fit in VRAM, and running out there either crashes the run or forces a smaller batch. If you are still choosing a card, our GPU buying guide for local AI work compares cards by VRAM per dollar, which is the number that decides what you can train.

Check which Python your Ubuntu ships

The install path depends on whether TensorFlow publishes a wheel for your system Python. pip only picks a pre-release when no stable version matches. On Python 3.14 it falls through to the next release’s first release candidate, which is the only TensorFlow build on PyPI with a cp314 wheel. Asking for the stable version by number makes the gap obvious:

TensorFlow install on Ubuntu 26.04: pip on Python 3.14 finds only 2.22.0rc0, uv with Python 3.13 installs 2.21.0

The unpinned install in that capture pulled tensorflow-2.22.0rc0 and keras-nightly-3.16.0.dev2026101704, and both imported cleanly. That is why it’s easy to miss, since nothing errors until a nightly Keras change breaks your code next week. The two LTS releases compare like this.

UbuntuSystem PythonStable TensorFlow wheelWhat pip install tensorflow gives youInstall path
26.04 LTS3.14.4No2.22.0rc0 + keras-nightlyuv with Python 3.13, or Docker
24.04 LTS3.12.3Yes2.21.0 + keras 3.15.1venv + pip

Check yours with python3 --version (our Python on Ubuntu 26.04 guide explains what the release ships). If it prints 3.10 to 3.13, follow the pip section. If it prints 3.14, use uv, which downloads a standalone Python 3.13 into your project and leaves the system Python alone. Ubuntu 26.04 has no python3.13 package to fall back on, so uv (or the Docker image) is the clean route there. Ubuntu 22.04 ships Python 3.10, which is still on TensorFlow’s supported list, so the pip path should apply there too, although we did not test it this time.

Install TensorFlow on Ubuntu 24.04 with pip

Ubuntu’s Python is marked externally managed, so pip refuses to install into it system-wide, and you would not want it to anyway, because TensorFlow pins its own NumPy, protobuf and grpcio versions. A virtual environment keeps all of that in one directory you can delete. Install the venv module first.

sudo apt update
sudo apt install -y python3-venv python3-pip

Create a project directory with its own environment and activate it. Every later command in this guide assumes you are inside ~/tf-project with the environment active, and your prompt shows (.venv) when it is:

mkdir -p ~/tf-project && cd ~/tf-project
python3 -m venv .venv
source .venv/bin/activate

Upgrade pip inside the environment, then install TensorFlow. The package and its dependencies come to about 2.1 GB on disk, and the download took a little over three minutes on our link.

pip install --upgrade pip
pip install tensorflow

Confirm which TensorFlow and Keras versions landed, so you know you got a stable build and not a release candidate:

python -c "import tensorflow as tf, keras; print(tf.__version__, keras.__version__)"

The last line of the output carries the two versions. A rc or dev suffix here means your Python is outside the supported range:

2.21.0 3.15.1

If you only ever train on the CPU, pip install tensorflow-cpu installs the same release without the CUDA plumbing. It took 1.5 GB against 2.1 GB for the full package in our lab, which matters in container images more than on a workstation.

Install TensorFlow on Ubuntu 26.04 with uv

uv solves the Python 3.14 problem by fetching a prebuilt CPython 3.13 per project, so you don’t need a PPA or a source build. It also resolves and installs faster than pip: the same TensorFlow install finished in 1 minute 26 seconds. Install uv for your user and load its PATH entry into the current shell.

curl -LsSf https://astral.sh/uv/install.sh | sh
source ~/.local/bin/env

Create the project environment on Python 3.13. The first run downloads the interpreter (about 34 MB) into ~/.local/share/uv/python, where every uv project on the account can reuse it.

mkdir -p ~/tf-project && cd ~/tf-project
uv venv --python 3.13 .venv
source .venv/bin/activate

uv reports the interpreter it picked and where the environment lives:

Using CPython 3.13.16
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate

Install TensorFlow with uv pip. A uv environment has no pip of its own, so every install in it goes through uv:

uv pip install tensorflow

Unlike pip on 3.14, uv on 3.13 resolves the stable release. The relevant lines from the install summary:

 + keras==3.15.1
 + numpy==2.5.4
 + tensorflow==2.21.0

We ran the same uv steps on Ubuntu 24.04 too, and they produced the same 2.21.0 install on Python 3.13.16. If you want one set of commands for both releases, uv is it. Readers who would rather not install anything outside apt can skip ahead to the Docker section, which needs no Python on the host at all.

Verify TensorFlow on the CPU

The official check is a single tensor operation. If it returns a tensor, the binary loaded and the CPU kernels work. Run it inside the activated environment.

python -c "import tensorflow as tf; print(tf.reduce_sum(tf.random.normal([1000, 1000])))"

The result is a random sum, so your number will differ. On a machine with no NVIDIA driver, TensorFlow also prints several log lines before it, including one tagged E0000. This is the full output from the 26.04 VM:

WARNING: All log messages before absl::InitializeLog() is called are written to STDERR
I0000 00:00:1792271032.757460   30346 cudart_stub.cc:31] Could not find cuda drivers on your machine, GPU will not be used.
I0000 00:00:1792271032.812023   30346 cpu_feature_guard.cc:227] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
To enable the following instructions: AVX2 FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
WARNING: All log messages before absl::InitializeLog() is called are written to STDERR
I0000 00:00:1792271033.955308   30346 cudart_stub.cc:31] Could not find cuda drivers on your machine, GPU will not be used.
E0000 00:00:1792271034.133497   30346 cuda_platform.cc:52] failed call to cuInit: INTERNAL: CUDA error: Failed call to cuInit: UNKNOWN ERROR (303)
tf.Tensor(515.70935, shape=(), dtype=float32)

None of those lines is a fault on a CPU-only box. The full tensorflow wheel includes the CUDA loader, so it looks for a driver, finds none and says so, and the AVX2/FMA line is advice about rebuilding from source, which you can ignore. To keep training logs readable, set TF_CPP_MIN_LOG_LEVEL=3 before Python starts. In our tests that removed the cuInit and CPU feature lines, but four lines still appear: two absl notices and two cudart_stub messages, all printed before the log level is read:

export TF_CPP_MIN_LOG_LEVEL=3
python -c "import tensorflow as tf; print(tf.config.list_physical_devices())"

A CPU-only machine lists one device. On a working GPU install you would see a GPU:0 entry here as well:

[PhysicalDevice(name='/physical_device:CPU:0', device_type='CPU')]

Add the export line to ~/.bashrc if you want it in every new shell, or leave it off while you debug, since the same mechanism hides real warnings too.

Train a test model

An import check proves the library loads, not that training works end to end, so run a small model as well. This script trains a two-layer network on MNIST for five epochs and times the training. It downloads the 11 MB dataset to ~/.keras/datasets on first run. Open a new file in the project directory.

vim ~/tf-project/mnist_test.py

Paste the script. It uses tf.keras.Input for the input shape, because Keras 3 warns when you pass input_shape to the first layer the way older tutorials do:

import time
import tensorflow as tf

print("TensorFlow", tf.__version__, "| devices:", [d.device_type for d in tf.config.list_physical_devices()])

(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

model = tf.keras.Sequential([
    tf.keras.Input(shape=(28, 28)),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(128, activation="relu"),
    tf.keras.layers.Dropout(0.2),
    tf.keras.layers.Dense(10),
])

model.compile(
    optimizer="adam",
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=["accuracy"],
)

start = time.perf_counter()
model.fit(x_train, y_train, epochs=5, validation_split=0.1, verbose=2)
print(f"Training time: {time.perf_counter() - start:.1f} s")

test_loss, test_acc = model.evaluate(x_test, y_test, verbose=0)
print(f"Test accuracy: {test_acc:.4f}")

Run it from the activated environment.

cd ~/tf-project
python mnist_test.py

Accuracy should climb every epoch and finish near 98% on the validation split. This excerpt is the run from the Ubuntu 26.04 uv environment, with the startup log lines, the dataset download progress bar and the memory allocator warnings removed:

TensorFlow 2.21.0 | devices: ['CPU']
Epoch 1/5
1688/1688 - 5s - 3ms/step - accuracy: 0.9058 - loss: 0.3180 - val_accuracy: 0.9628 - val_loss: 0.1355
Epoch 2/5
1688/1688 - 4s - 2ms/step - accuracy: 0.9547 - loss: 0.1527 - val_accuracy: 0.9697 - val_loss: 0.1022
Epoch 3/5
1688/1688 - 5s - 3ms/step - accuracy: 0.9643 - loss: 0.1153 - val_accuracy: 0.9777 - val_loss: 0.0798
Epoch 4/5
1688/1688 - 4s - 2ms/step - accuracy: 0.9716 - loss: 0.0907 - val_accuracy: 0.9780 - val_loss: 0.0780
Epoch 5/5
1688/1688 - 4s - 2ms/step - accuracy: 0.9752 - loss: 0.0792 - val_accuracy: 0.9792 - val_loss: 0.0754
Training time: 22.3 s
Test accuracy: 0.9766

We ran the same script under each install method on the same 2 vCPU host type, and the numbers barely moved:

SetupPython5 epochsPer epochTest accuracy
Ubuntu 26.04, uv venv3.13.1622.3 s4-5 s0.9766
Ubuntu 24.04, venv + pip3.12.321.5 s4-5 s0.9760
Ubuntu 26.04, tensorflow/tensorflow:latest3.11.1522.4 s4-6 s0.9769

The spread between methods is smaller than the run-to-run noise: a repeat of the 24.04 venv run took 24.6 s against 21.5 s in the table, so differences of about 3 seconds mean nothing here. The install method is a packaging decision, not a speed one, and the container costs you nothing at training time. Your own times will scale with core count, so treat these as the baseline for 2 vCPUs of a 2015 desktop CPU, not a target.

Add GPU support

GPU support on Linux needs a working NVIDIA driver on the host and the CUDA libraries TensorFlow links against. The [and-cuda] extra installs those libraries as pip packages inside the environment, so you don’t install the CUDA toolkit system-wide. Our lab has no GPU, so the commands below were checked against the TensorFlow pip install docs and Ubuntu’s driver docs, and only the package install was run.

Check for a driver first. If nvidia-smi prints your card and a driver version of 525.60.13 or newer, skip the install:

nvidia-smi

Without a driver, let Ubuntu pick the right one. Ubuntu’s docs recommend the --gpgpu variant on servers and compute boxes, which installs the headless -server driver without the utilities package, so nvidia-smi is missing until you add nvidia-utils for the same branch. Note the branch number from the list command, install the matching utils, then reboot so the kernel module loads. Our NVIDIA driver and CUDA walkthrough covers Secure Boot and the manual route if this fails:

sudo apt install -y ubuntu-drivers-common
sudo ubuntu-drivers list --gpgpu
sudo ubuntu-drivers install --gpgpu

580 is only the branch in this example; use the branch the list command printed, since 24.04 also offered 595 and 615 headless server packages when we checked. On a desktop, run sudo ubuntu-drivers install without --gpgpu instead, which pulls the utilities in with the driver, and skip straight to the reboot:

sudo apt install -y nvidia-utils-580-server
sudo reboot

With the driver in place, install TensorFlow with the CUDA extra in the activated environment. In a uv environment (Ubuntu 26.04) the quotes stop your shell from treating the brackets as a glob:

uv pip install "tensorflow[and-cuda]"

In a plain venv on Ubuntu 24.04, use pip with the same quoting:

pip install "tensorflow[and-cuda]"

We ran this install on the CPU-only VM to see what it pulls. It added twelve NVIDIA packages and grew the environment to 6.2 GB. The excerpt below shows five of the thirteen packages it listed. The versions are newer than the TensorFlow docs page lists (CUDA 12.3, cuDNN 8.9.7), because the extra sets ranges such as nvidia-cudnn-cu12>=9.3.0.75,<10.0 and caps the CUDA packages below 13.0, so the resolver takes the newest 12.x builds inside them.

 + nvidia-cublas-cu12==12.9.2.10
 + nvidia-cuda-runtime-cu12==12.9.79
 + nvidia-cudnn-cu12==9.27.0.42
 + nvidia-nccl-cu12==2.32.3
 + tensorflow==2.21.0

Confirm TensorFlow sees the card. On a GPU host this should print a GPU:0 device; our VM printed an empty list, which is also what you get when the driver is missing or too old:

python -c "import tensorflow as tf; print(tf.config.list_physical_devices('GPU'))"

TensorFlow grabs nearly all GPU memory when it starts, which breaks a second process on the same card and makes out-of-memory errors hard to read. Turn on memory growth at the top of a training script, before any other TensorFlow call, so it allocates as it goes.

import tensorflow as tf
for gpu in tf.config.list_physical_devices("GPU"):
    tf.config.experimental.set_memory_growth(gpu, True)

If the device list stays empty with a working nvidia-smi, the TensorFlow docs put it down to components that “aren’t being detected, and/or conflict with the existing system CUDA installation”. They give two optional fixes to run inside the activated environment. The first symlinks the pip-installed NVIDIA libraries into the TensorFlow package directory:

pushd $(dirname $(python -c 'print(__import__("tensorflow").__file__)'))
ln -svf ../nvidia/*/lib/*.so* .
popd

The second puts the pip-installed ptxas on the environment’s PATH, which XLA needs to compile kernels. Both come from the Linux section of the install docs and, like the rest of this GPU section, were not run on hardware here:

ln -sf $(find $(dirname $(dirname $(python -c "import nvidia.cuda_nvcc; print(nvidia.cuda_nvcc.__file__)"))/*/bin/) -name ptxas -print -quit) $VIRTUAL_ENV/bin/ptxas

Check echo $LD_LIBRARY_PATH for a system CUDA path first, because a leftover toolkit there is the usual conflict.

Run TensorFlow in Docker

The official images bundle Python 3.11 and the stable TensorFlow, so the host’s Python version stops mattering, which makes Docker the least fiddly option on Ubuntu 26.04. Ubuntu’s own docker.io package is enough for this; the Docker CE install guide covers Docker’s own repository if you want upstream release timing instead of Ubuntu’s. Add your user to the docker group, then log out and back in for it to apply. Membership is root-equivalent on that host, so only add accounts you would trust with sudo.

sudo apt install -y docker.io
sudo usermod -aG docker $USER

Pull the CPU image and ask it for its Python and TensorFlow versions.

docker run --rm tensorflow/tensorflow:latest python3 -c "import sys, tensorflow as tf; print(sys.version.split()[0], tf.__version__)"

Both versions print after the usual CPU feature notice:

3.11.15 2.21.0

To run your own code, mount the project directory into the container. This runs the same MNIST script from the previous section and produced the Docker row in the timing table:

docker run --rm -e TF_CPP_MIN_LOG_LEVEL=3 -v ~/tf-project:/workspace -w /workspace tensorflow/tensorflow:latest python3 mnist_test.py

The images are large, so pull only the tag you need. These are the sizes docker images reported in our lab:

  • tensorflow/tensorflow:latest: 2.91 GB
  • tensorflow/tensorflow:latest-jupyter: 3.51 GB
  • tensorflow/tensorflow:latest-gpu: 11.5 GB

The GPU image also needs the NVIDIA Container Toolkit on the host, because Docker has no way to pass a GPU into a container without it. These are the repository and install commands from NVIDIA’s install guide. We ran them on Ubuntu 26.04, where the repository installed toolkit 1.20.1 and wrote the nvidia runtime into /etc/docker/daemon.json:

curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
  sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
  sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt update
sudo apt install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

On a host with an NVIDIA driver, this lists the GPU from inside the container. We could not run it to success without a card, and the two errors it gives on a box that is missing a piece are in the troubleshooting section:

docker run --rm --gpus all tensorflow/tensorflow:latest-gpu python3 -c "import tensorflow as tf; print(tf.config.list_physical_devices('GPU'))"

A working setup prints the same GPU:0 device list as the pip check above.

Use TensorFlow in Jupyter

Install JupyterLab into the same environment as TensorFlow, so notebooks use that interpreter without registering a separate kernel. In a uv environment that is uv pip install; in a 24.04 venv use pip install jupyterlab:

uv pip install jupyterlab

Start it bound to localhost. Jupyter runs code as your user, and its token is the only thing standing between the network and a shell, so opening port 8888 in UFW (the old advice) exposes far more than a notebook:

jupyter lab --no-browser --ip 127.0.0.1 --port 8888

On a remote server, reach it through an SSH tunnel from your workstation, then open the http://127.0.0.1:8888/lab?token=... URL that Jupyter printed. Replace the user and address with your server’s.

ssh -L 8888:127.0.0.1:8888 [email protected]

We executed a two-cell notebook in this environment with jupyter nbconvert --execute. The first cell printed 2.21.0 and the CPU device, and the second returned a 3×3 tf.Tensor from a matrix multiply, so the kernel runs TensorFlow the same way the shell does. Our Jupyter setup guide covers running it as a service with a password.

The Jupyter image is the alternative when you skipped the Python setup. Publish the port on localhost only and mount your project into /tf/work. The container runs as root, so notebooks you save into that mount show up root-owned on the host and need sudo chown before you edit them outside Docker.

docker run -d --name tf-jupyter -p 127.0.0.1:8888:8888 -v ~/tf-project:/tf/work tensorflow/tensorflow:latest-jupyter

The login token is in the container log. The image ships both Notebook 7 and JupyterLab. The printed URL opens Notebook 7 at /tree, and swapping that for /lab with the same token opens JupyterLab; both returned 200 in our test:

docker logs tf-jupyter 2>&1 | grep 127.0.0.1

Only one of the two can hold port 8888 at a time, so stop the venv Jupyter before starting the container, or map the container to another host port such as 127.0.0.1:8889:8888.

Fix common TensorFlow install errors

Each of these came up in the lab, either on Ubuntu 26.04’s Python or on a host without the GPU pieces in place.

ERROR: No matching distribution found for tensorflow==2.21.0

pip is running on a Python with no stable TensorFlow wheel, and the line before it names the only candidate it found (from versions: 2.22.0rc0). On Ubuntu 26.04 that is the system Python 3.14. Recreate the environment on Python 3.13 with uv as shown above, or use the Docker image. Forcing the release candidate with --pre works, but it brings a nightly Keras along with it.

could not select device driver “” with capabilities: [[gpu]]

Docker printed this for docker run --gpus all before the NVIDIA Container Toolkit was installed: there is no runtime that knows how to hand a GPU to the container. Install the toolkit, run sudo nvidia-ctk runtime configure --runtime=docker and restart Docker.

libnvidia-ml.so.1: cannot open shared object file

With the toolkit installed but no NVIDIA driver on the host, the same docker run failed in the prestart hook:

nvidia-container-cli: initialization error: load library failed: libnvidia-ml.so.1: cannot open shared object file: no such file or directory

The library comes with the driver, not with the toolkit. Install the driver with ubuntu-drivers, reboot and confirm nvidia-smi works on the host before you try the container again.

failed call to cuInit: UNKNOWN ERROR (303)

On a machine with no NVIDIA GPU this is expected noise from the full tensorflow wheel, and training still runs on the CPU. Silence it with TF_CPP_MIN_LOG_LEVEL=3, or install tensorflow-cpu instead. On a machine that does have a GPU, the same line means the driver is not loaded, so check nvidia-smi and whether the reboot after the driver install happened.

Pick the install method for your box

The training numbers were the same for every method, so choose on packaging and on how much of the host you want to touch:

  • Ubuntu 24.04 workstation: venv with pip
  • Ubuntu 26.04 workstation: uv with Python 3.13
  • CPU-only server or CI image: tensorflow-cpu in either of the above
  • Host Python must stay untouched: the official Docker image
  • Same GPU stack on every machine: latest-gpu with the container toolkit

If you want one habit for both releases, keep uv, because the same commands land on the stable release whatever Python Ubuntu ships. Whichever you pick, read the version line after the install. A rc or dev suffix means pip found your Python too new, and the fix is a different interpreter, not --pre. Once a stable TensorFlow release publishes Python 3.14 wheels, the plain venv route will work on Ubuntu 26.04 as well.

Keep reading

Upgrade Ubuntu 24.04 to Ubuntu 26.04 LTS (Step by Step) Ubuntu Upgrade Ubuntu 24.04 to Ubuntu 26.04 LTS (Step by Step) UFW Firewall Commands with Examples on Ubuntu 26.04 / 24.04 Security UFW Firewall Commands with Examples on Ubuntu 26.04 / 24.04 Ollama Models Cheat Sheet: Best Models by VRAM, Tested (PDF) AI Ollama Models Cheat Sheet: Best Models by VRAM, Tested (PDF) Upgrade a Single Package on Ubuntu / Debian with apt Debian Upgrade a Single Package on Ubuntu / Debian with apt APT Cheat Sheet: apt and apt-get Commands for Ubuntu/Debian Cheat Sheets APT Cheat Sheet: apt and apt-get Commands for Ubuntu/Debian How To Install RabbitMQ on Ubuntu 24.04 (Noble Numbat) Ubuntu How To Install RabbitMQ on Ubuntu 24.04 (Noble Numbat)

Leave a Comment

Press ESC to close