Self-hosted service
iot-salzburg/gpu-jupyter avatar
iot-salzburg/gpu-jupyter

GPU-Jupyter: a JupyterLab image where the Dockerfile is generated, not written

GPU-Jupyter: Your GPU-accelerated JupyterLab with a rich data science toolstack, TensorFlow and PyTorch for your reproducible deep learning experiments.

768 stars239 forksJupyter NotebookApache-2.0

At a glance

What is it?
GPU-Jupyter builds Docker images for GPU-accelerated JupyterLab on top of NVIDIA's CUDA base image and Jupyter's Docker Stacks, with a script that assembles the Dockerfile and tags that follow the CUDA and Ubuntu versions. It suits research teams that want reproducible GPU notebooks, and it does not suit anyone without an NVIDIA GPU and the NVIDIA Container Toolkit.
Who is it for?
gpu-jupyter is worth the setup for a research group that runs PyTorch or TensorFlow on NVIDIA hardware and wants every experiment to start from the same image, because the CUDA and Ubuntu versions are carried in the image tag and the Dockerfile is generated from a script rather than hand-maintained. It is the wrong tool for a CPU-only machine, for a team that wants a JupyterLab with a narrow, pinned dependency set, or for anyone who cannot run containers with GPU access.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 75 days ago.
What is it written in?
Mainly Jupyter Notebook, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The Dockerfile is a build artifact, which is the whole point

Most GPU container images are a Dockerfile someone wrote, pinned to versions that quietly go stale. gpu-jupyter takes the opposite approach. The repository contains generate-Dockerfile.sh, a custom/ directory for your own additions, a .build/ directory where the generated Dockerfile lands, and build_push_all.sh to push the results. The image is assembled by a script from a small set of inputs, so the same inputs always produce the same image.

The base stack is documented in the README: NVIDIA's official CUDA Docker image underneath, and Jupyter's Docker Stacks on top of it. The README links to the Jupyter Docker Stacks project directly, and credits the project with the ability to work with version control for source code, tagged data spaces and seeds for random functions inside isolated containers, which it presents as the basis for reproducible and shareable experiments. A companion repository, reproducible-research-with-gpu-jupyter, is pointed at as a one-command example of the workflow.

The practical upshot is that your environment is a published artifact with a name like a release, not a set of instructions someone has to follow in the right order. That is a real difference from the usual Dockerfile in a repository, and it is also a constraint: the layers you get are the ones the script decided on.

The quickstart is one docker run and a token in the terminal

The README puts a single command at the top under a TL;DR heading, and it is the fastest way to see what the project does:

bash
mkdir -p data
docker run --gpus all -it -p 8848:8888 -v "$(pwd)/data:/home/jovyan/work" --user root cschranz/gpu-jupyter:v1.11_cuda-13.0_ubuntu-24.04_python-only

The command creates a data directory, asks Docker for access to all GPUs, publishes container port 8888 on host port 8848, mounts the local data directory at the working path inside the container, and runs the image as root. The README then tells you to open http://localhost:8848 and use the token printed in the terminal. The image name encodes the release, the CUDA version, the Ubuntu version and the flavour, here python-only, so the environment you run is stated in the command itself.

The longer form in the Quickstart section adds two environment variables and shows the mount path explicitly:

bash
   cd your-working-directory
   ll data  # this path will be mounted by default
   docker run --gpus all -it -p 8848:8888 -v "$(pwd)/data:/home/jovyan/work" -e GRANT_SUDO=yes -e JUPYTER_ENABLE_LAB=yes --user root cschranz/gpu-jupyter:v1.11_cuda-13.0_ubuntu-24.04_python-only

GRANT_SUDO grants sudo inside the container and JUPYTER_ENABLE_LAB turns on JupyterLab, which is the interface most people actually want from this image. The README warns that the first pull will take a while, since the whole data science environment has to download.

The GPU check is a container run, not a host command

Before anything else, the host has to satisfy four requirements: an NVIDIA GPU with drivers, CUDA, Docker, and the NVIDIA Container Toolkit. The README offers a way to confirm all of them at once, by running the CUDA image with nvidia-smi inside it:

bash
    docker run --rm --gpus all nvidia/cuda:13.0.3-cudnn-runtime-ubuntu24.04 nvidia-smi

The expected output shown in the README is an nvidia-smi table with the driver and CUDA versions, the GPU name, memory usage and a processes list. If that command prints a table, the toolchain is wired up. If it fails, the README points to an installation script in the extra/ directory and to a written guide for setting up a GPU JupyterLab on Docker, and it notes that a cloud provider is an option for people without an NVIDIA machine.

That last point deserves emphasis, because it is the boundary of the project. The images are built for NVIDIA GPUs and the runtime path depends on the container runtime being able to pass GPU devices through. On a laptop without an NVIDIA card, or on a cluster where the container toolkit is not installed, these images do not run, and the README's answer is a cloud notebook rather than a local fallback.

The compose file shows how the pieces are meant to fit together

A docker-compose.yml in the repository root describes the service the same way the quickstart does, but with the settings a longer-running installation needs. The device reservation block asks for GPU capability, with a commented line for selecting a single GPU by device id and a commented limits block for capping CPU and memory. The port mapping, the volume and the environment variables match the CLI example:

yaml
    ports:
      - 8848:8888
    volumes:
      - ./data:/home/jovyan/work
    environment:
      GRANT_SUDO: "yes"
      JUPYTER_ENABLE_LAB: "yes"
      NB_UID: ${JUPYTER_UID:-1000}
      NB_GID: ${JUPYTER_GID:-1000}
    env_file:
      - path: token.env
        required: false
    # enable sudo permissions

Two details matter here. The NB_UID and NB_GID variables default to 1000 and can be overridden from the environment, which is how you align file ownership in the mounted data directory with the host user. And the env_file entry points at a token.env file that is marked optional, which is how a static token is supplied: the README has a section on setting a static token, and the compose file is the place where that file gets wired in. The service also sets restart to unless-stopped and runs as root, matching the flags used in the quickstart command.

Releases are named after the CUDA and Ubuntu versions underneath

The release list is the clearest statement of the project's operating model. The most recent release is Release-v1.11_cuda-13.0_ubuntu-24.04, dated 2026-07-23. Before it came Release-v1.10_cuda-12.9_ubuntu-24.04 on 2026-01-09, and before that Release-v1.9_cuda-12.6_ubuntu-24.04 on 2025-02-28. Each tag states the project version, the CUDA toolkit version and the Ubuntu base, and the image published on Docker Hub carries the same string with a flavour suffix such as python-only.

The consequence for an adopter is that upgrading is a decision about CUDA, not about Python packages. Moving from the 12.9 image to the 13.0 image is a driver and toolkit decision that your host has to support, which is why the README makes the nvidia-smi check a prerequisite rather than an optional extra. The gap between the January and July releases is about six months, and the gap before that was longer, so the cadence is tied to CUDA releases rather than to the project itself.

The repository also has a section on customizing the installation and a section on sharing a customized Dockerfile, and a build_push_all.sh script at the top level, so the intended path for teams with their own requirements is to build on the generator and publish their own images rather than to patch the published ones.

Where GPU-Jupyter is the wrong tool

The first limit is hardware, and it is absolute. There is no CPU variant mentioned in the README, and the check command in the quickstart section requires --gpus all. A team on macOS, on a CPU-only server, or on a cluster with AMD or Intel accelerators gets nothing from this project, and the answer in the documentation is a cloud provider rather than a local image.

The second limit is the size and shape of the environment. The image carries a full data science stack with PyTorch and TensorFlow, and the flavour in the example tag is python-only, which suggests the tag carries the choice of which stack to install. A team that needs a small, exactly pinned environment for a production service will find that a large research image is the wrong shape, because every image update moves the versions of a great many packages at once.

The third limit is the running-as-root detail. Both the quickstart command and the compose file use --user root and the compose file enables sudo permissions, which is convenient inside a throwaway research container and a poor default for anything that processes untrusted notebooks. The README does not document a hardened, non-root deployment, and it does not document how to change the published token default other than through the static token file.

Compared with the stacks it is built on

The alternative is to use the layers directly. Jupyter's Docker Stacks on their own give you a documented set of images with a known set of tools, and the README names them as the foundation. Add a CUDA base yourself and you get the same GPU access with a Dockerfile you control, at the cost of redoing the pinning work every time you want a new CUDA version. The difference in approach is that gpu-jupyter turns that recurring work into a script with published, dated outputs, and the price is that you accept the versions the script chose.

A second alternative is a managed notebook service, which the README itself points to for people without an NVIDIA GPU. That path gives you a working environment without a container runtime on your machine, but the environment stops being an artifact you can rebuild, which cuts against the reproducibility argument the project is built on.

A third is a plain image from a framework vendor, where the CUDA base and the framework are already paired for you. gpu-jupyter is broader: the README describes PyTorch, TensorFlow and a wider data science toolstack in one image, so the cost of the broader environment is a larger download and more versions moving at once.

Editorial conclusion

gpu-jupyter is worth the setup for a research group that runs PyTorch or TensorFlow on NVIDIA hardware and wants every experiment to start from the same image, because the CUDA and Ubuntu versions are carried in the image tag and the Dockerfile is generated from a script rather than hand-maintained. It is the wrong tool for a CPU-only machine, for a team that wants a JupyterLab with a narrow, pinned dependency set, or for anyone who cannot run containers with GPU access. Before you commit, check three things the README leaves open: that your driver and CUDA version fall inside the range the tagged images cover, that the NVIDIA Container Toolkit is installed on the host rather than assumed, and that your own additions go into the custom/ directory, since the generated Dockerfile under.build/ is overwritten on every regeneration.

Frequently asked questions

How do I start GPU-Jupyter on my machine?

The README's quickstart is a single docker run that asks for GPU access, publishes port 8848, mounts a local data directory at /home/jovyan/work and runs the cschranz/gpu-jupyter image as root, then you open http://localhost:8848 and use the token printed in the terminal.

What does the GPU-Jupyter image tag mean?

Tags carry the project version, the CUDA version, the Ubuntu base and the flavour, for example v1.11_cuda-13.0_ubuntu-24.04_python-only. The published releases follow the same pattern, with Release-v1.11_cuda-13.0_ubuntu-24.04 dated 2026-07-23 the most recent.

Can I use GPU-Jupyter without an NVIDIA GPU?

The README requires an NVIDIA GPU with drivers, CUDA, Docker and the NVIDIA Container Toolkit, and the verification command runs nvidia-smi with --gpus all. For people without an NVIDIA GPU the README points to a cloud solution instead of a local image.

Official sources

  1. iot-salzburg/gpu-jupyter on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/iot-salzburg-gpu-jupyter.svg)](https://hysenlabs.com/projects/iot-salzburg-gpu-jupyter)

Was this useful?