OKDP Jupyter Docker images built from the upstream jupyter/docker-stacks Dockerfiles, with an OKDP-maintained version compatibility matrix. The pyspark-notebook and all-spark-notebook images bundle Apache Spark binaries from the OKDP Spark distribution instead of archive.apache.org. The images are published to quay.io/okdp/jupyter and used as the singleuser image of the OKDP sandbox JupyterHub.
OKDP/jupyterlab-docker layers on top of jupyter/docker-stacks to ship JupyterLab images tailored for the OKDP data platform: a curated set of Python / Spark / Java / Scala combinations, Spark binaries hosted by OKDP, multi-arch builds, and a handful of data-engineering libraries pre-installed.
- Curated compatibility matrix:
.build/.versions.ymlpins the Python x Spark x Java x Scala x Hadoop combinations OKDP actually builds and tests (e.g. Python 3.10/3.11/3.12 against Spark 3.3.4, 3.4.2, 3.5.6 on Java 17 with Scala 2.12/2.13), spanning Spark 3.2.x through 4.0.x so you don't have to figure out which versions are mutually compatible. - Spark tarballs from
OKDP/spark-images: instead of pulling fromarchive.apache.org, every entry in the matrix setsspark_download_urltohttps://github.com/OKDP/spark-images/releases/download/spark-tarballs/, giving stable, OKDP-controlled artifacts that survive upstream archive churn. - Multi-arch images (
linux/amd64+linux/arm64): published since v1.1.0, so the same tags run on x86 CI runners, x86 Kubernetes clusters, and Apple Silicon laptops without a separate build. - Extra Python packages baked into scipy-notebook and downstream images:
scipy-notebook/requirements.txtaddsjupyter-fs[fsspec],s3fs,jupysql,trino,sqlalchemy-trinoandnbgitpulleron top of the upstream layer, so S3 browsing, Trino/SQL notebooks, and Git-based notebook sync work out of the box on OKDP. - OKDP override layer and custom Python extensions: each image directory (e.g.
scipy-notebook/Dockerfile,pyspark-notebook/Dockerfile) is a thinCOPY requirements.txt+pip installlayer on top of the upstream stage, while.build/python/src/okdp/carries OKDP-specific tooling:extension/matrixexpands the compatibility matrix into a build matrix,extension/tagginggenerates the image tags, andpatch/applies fixes against the upstreamdocker-stackssources.
This repository builds and publishes a matrix of JupyterLab / JupyterHub container images. It vendors the upstream jupyter/docker-stacks source as a git-subtree under docker-stacks/ and adds:
- An OKDP-maintained version compatibility matrix (
.build/.versions.yml): combinations of Python, Spark, Java, Scala known to work together. - Spark binaries from the OKDP Spark distribution: for
pyspark-notebookandall-spark-notebook, the Spark tarballs are downloaded fromOKDP/spark-imagesGitHub releases (seespark_download_urlin.build/.versions.yml) instead ofarchive.apache.org/dist/spark/. - Multi-arch images for
linux/amd64andlinux/arm64(since v1.1.0). - Local override layer:
scipy-notebook/Dockerfileadds extra Python packages fromscipy-notebook/requirements.txt(jupyter-fs[fsspec],s3fs,jupysql,trino,sqlalchemy-trino,nbgitpuller) on top of the upstreamscipy-notebookDockerfile.pyspark-notebook/Dockerfileis wired the same way with an emptypyspark-notebook/requirements.txtas a placeholder. Packages are inherited transitively fromscipy-notebook. - Custom Python extensions under
.build/python/src/okdp/: tagging, compatibility-matrix expansion, patches and unit tests (see Development).
OKDP Jupyter images follow the upstream jupyter/docker-stacks inheritance model: each image extends a parent, adding a focused set of libraries on top of the previous layer. The diagram below illustrates that chain, from the minimal docker-stacks-foundation up to specialized variants such as all-spark-notebook, r-notebook, and datascience-notebook. OKDP applies a thin override layer on top of scipy-notebook and pyspark-notebook to pin the additional dependencies declared in OKDP's requirements.txt.
For the canonical, upstream-maintained description of every image and what it ships, refer to the Jupyter Docker Stacks reference: Selecting an Image.
The OKDP override layer (highlighted on scipy-notebook and pyspark-notebook) is where OKDP-specific packages from requirements.txt are installed on top of the upstream image content.
Before running the images in this repository, make sure your host meets the following baseline. The values below reflect the environment that was used to end-to-end test the reference image.
- Docker with BuildKit enabled (recommended for faster, more reliable builds). Tested version: Docker Desktop 28.2.2 on macOS arm64.
- Free disk space: at least ~7 GB available for the
pyspark-notebookimage on disk after pull. Allow additional headroom for layer caches, build artifacts, and mounted workspace data. - Free TCP port 8888 on the host, used to expose the JupyterLab UI. If the port is already in use, remap it at run time with
-p <host-port>:8888. - Tested image (pulled and end-to-end tested locally on 2026-04-13):
- Tag:
quay.io/okdp/jupyter/pyspark-notebook:spark-3.5.6-python-3.11-java-17-scala-2.13 - Digest:
sha256:e1662ba16c81d4eddc636b33aaabb7aedd74aef25253e6a4327f20fdcabb69f8
- Tag:
To pin the exact tested artifact (recommended for reproducibility), reference the image by digest:
docker pull quay.io/okdp/jupyter/pyspark-notebook@sha256:e1662ba16c81d4eddc636b33aaabb7aedd74aef25253e6a4327f20fdcabb69f8Other host operating systems and Docker versions are expected to work but have not been verified against this baseline.
Pull and run the pyspark-notebook image, the canonical OKDP image, which bundles Apache Spark, Java 17, Scala 2.13 and the scientific Python stack:
docker pull quay.io/okdp/jupyter/pyspark-notebook:spark-3.5.6-python-3.11-java-17-scala-2.13
docker run --rm -p 8888:8888 \
quay.io/okdp/jupyter/pyspark-notebook:spark-3.5.6-python-3.11-java-17-scala-2.13The container starts JupyterLab on port 8888 (via the upstream start-notebook.py entrypoint) and prints a one-time access URL containing a token, for example:
[I ServerApp] Jupyter Server 2.x is running at:
[I ServerApp] http://localhost:8888/lab?token=<random-token>
[I ServerApp] http://127.0.0.1:8888/lab?token=<random-token>
Open the http://127.0.0.1:8888/lab?token=... URL in your browser to reach the JupyterLab UI.
All images are published to quay.io/okdp/jupyter. Pull syntax:
docker pull quay.io/okdp/jupyter/<image>:<tag>Where <image> is one of docker-stacks-foundation, base-notebook, minimal-notebook, scipy-notebook, r-notebook, datascience-notebook, pyspark-notebook, all-spark-notebook, and <tag> is any published tag (the recommended pyspark tag as of v1.3.0 is spark-3.5.6-python-3.11-java-17-scala-2.13).
When rebuilding an image from docker-stacks/images/<image>/Dockerfile, the following --build-arg values are honoured (extracted from the upstream Dockerfiles):
| Build arg | Used in | Default | Description |
|---|---|---|---|
REGISTRY |
all | quay.io |
Registry hosting the base image. |
OWNER |
all | jupyter |
Owner of the base image; set to okdp/jupyter to chain off OKDP images. |
BASE_IMAGE |
all | ${REGISTRY}/${OWNER}/<parent> |
Full base image reference. |
ROOT_IMAGE |
docker-stacks-foundation |
ubuntu:24.04 |
The Ubuntu base. |
NB_USER |
docker-stacks-foundation |
jovyan |
UNIX user inside the container. |
NB_UID |
docker-stacks-foundation |
1000 |
UID of NB_USER. |
NB_GID |
docker-stacks-foundation |
100 |
GID NB_USER belongs to. |
PYTHON_VERSION |
docker-stacks-foundation |
3.13 |
Python version installed in the conda env. |
openjdk_version |
pyspark-notebook |
17 |
OpenJDK major version. |
spark_version |
pyspark-notebook |
(required) | Apache Spark version to bundle. |
hadoop_version |
pyspark-notebook |
3 |
Hadoop major version embedded in the Spark tarball. |
scala_version |
pyspark-notebook |
(required) | Scala version. |
spark_download_url |
pyspark-notebook |
https://dlcdn.apache.org/spark/ |
Base URL for the Spark tarball; OKDP overrides this to https://github.com/OKDP/spark-images/releases/download/spark-tarballs/ via .build/.versions.yml#L30. |
The defaults work out of the box. The Quick Start does not set any env variable. Runtime configuration is inherited from upstream jupyter/docker-stacks: see common features for the full list of -e VAR=value flags (NB_USER, NB_UID, GRANT_SUDO, CHOWN_HOME, …) and the upstream pyspark-notebook page for SPARK_OPTS.
All images are published under quay.io/okdp/jupyter:
| Image | Description |
|---|---|
docker-stacks-foundation |
Minimal Ubuntu + mamba + jovyan user. Foundation layer for all other images. |
base-notebook |
Adds JupyterLab, JupyterHub single-user, and Notebook. |
minimal-notebook |
Adds common CLI tools (git, nano, tzdata, fonts). |
scipy-notebook |
Adds the scientific Python stack (pandas, scipy, matplotlib, scikit-learn, seaborn, …) + OKDP additions (scipy-notebook/requirements.txt): jupyter-fs[fsspec], s3fs, jupysql, trino, sqlalchemy-trino, nbgitpuller. |
r-notebook |
minimal-notebook + R + IRKernel. |
datascience-notebook |
scipy-notebook + R + Julia. |
pyspark-notebook |
scipy-notebook + Apache Spark (from the OKDP Spark distribution) + Java + Scala. |
all-spark-notebook |
pyspark-notebook + R (r-base, r-irkernel, r-sparklyr) for running R on Spark. |
ℹ️
julia-notebook,tensorflow-notebookandpytorch-notebookexist as scaffolding inbuild-datascience-images-template.ymlbut are currently commented out and not built or published.
The project builds the images with long-format tags. Each tag combines multiple compatible version combinations. The examples below show the canonical tags published as of v1.3.0 (build date 2026-04-13). Browse quay.io/okdp for the full list.
python-3.11-2026-04-13python-3.11.15-2026-04-13python-3.11.15-hub-5.4.4-lab-4.5.6python-3.11.15-hub-5.4.4-lab-4.5.6-2026-04-13
python-3.11-2026-04-13python-3.11.15-2026-04-13python-3.11.15-hub-5.4.4-lab-4.5.6python-3.11.15-hub-5.4.4-lab-4.5.6-2026-04-13python-3.11.15-r-4.5.3-julia-1.12.6-2026-04-13python-3.11.15-r-4.5.3-julia-1.12.6-hub-5.4.4-lab-4.5.6python-3.11.15-r-4.5.3-julia-1.12.6-hub-5.4.4-lab-4.5.6-2026-04-13
spark-3.5.6-python-3.11-java-17-scala-2.13spark-3.5.6-python-3.11-java-17-scala-2.13-2026-04-13spark-3.5.6-python-3.11.15-java-17.0.18-scala-2.13.8-hub-5.4.4-lab-4.5.6spark-3.5.6-python-3.11.15-java-17.0.18-scala-2.13.8-hub-5.4.4-lab-4.5.6-2026-04-13
The ci build pipeline is composed of the following workflows (the *-template.yml files under .github/workflows/ are reusable workflows called from ci.yml and publish.yml):
build-base-images-template:docker-stacks-foundation,base-notebook,minimal-notebook,scipy-notebookbuild-datascience-images-template:r-notebook,datascience-notebook(julia-notebook,tensorflow-notebookandpytorch-notebookare commented out)build-spark-images-template:pyspark-notebook,all-spark-notebookpublish: push the built images to the container registry (main branch only)auto-rerun: partially re-run jobs in case of failures (GitHub runner issues / main branch only)ci: run CI pipeline at every contribution
The build is driven by .build/.versions.yml. The build-matrix section defines which component versions to build; it filters the parent compatibility-matrix to limit the combinations actually built. Example:
build-matrix:
python_version: ['3.10', '3.11', '3.12']
spark_version: [3.3.4, 3.4.2, 3.5.6]
java_version: [17]
scala_version: [2.12, 2.13]→ produces these 6 compatible combinations (filtered against the compatibility-matrix; incompatible pairs like python 3.10 + spark 3.5.x or python 3.12 + spark 3.x are silently dropped):
spark3.3.4-python3.10-java17-scala2.12spark3.3.4-python3.10-java17-scala2.13spark3.4.2-python3.11-java17-scala2.12spark3.4.2-python3.11-java17-scala2.13spark3.5.6-python3.11-java17-scala2.12spark3.5.6-python3.11-java17-scala2.13
Development images with the -<GIT-BRANCH>-latest suffix (e.g. spark3.5.6-python3.11-java17-scala2.13-<GIT-BRANCH>-latest) are produced at every pipeline run, regardless of the git branch.
Official images are published to quay.io/okdp/jupyter at every release and periodically, every Monday at 05:00 GMT (to pick up upstream security updates; see the cron: "0 5 * * 1" schedule in publish.yml).
act can be used to run the workflows locally:
act --container-architecture linux/amd64 \
-W .github/workflows/ci.yml \
--env ACT_SKIP_TESTS=<true|false> \
--rm(use --container-architecture linux/amd64 on Apple Silicon (M1/M2/M3) Macs.)
With a pyspark-notebook container running (see Quick Start):
# Should return 200 (curl -L follows the redirect to the JupyterLab login page).
# A plain GET without -L returns 302; that is expected, Jupyter redirects to /login with the token.
# Note: -I (HEAD) returns 405 because Jupyter rejects HEAD on /lab.
curl -sL -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8888/lab200
For pyspark-notebook / all-spark-notebook, run a one-liner SparkPi job from inside the running container to confirm Spark is wired up:
CONTAINER_ID=$(docker ps --filter ancestor=quay.io/okdp/jupyter/pyspark-notebook:spark-3.5.6-python-3.11-java-17-scala-2.13 --format '{{.ID}}' | head -1)
docker exec "$CONTAINER_ID" bash -lc '
$SPARK_HOME/bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master "local[2]" \
$SPARK_HOME/examples/jars/spark-examples_*.jar 100 2>&1 | grep "Pi is roughly"
'Pi is roughly 3.1407...
CI runs the upstream docker-stacks tests at every pipeline trigger, plus the OKDP unit tests.
The PySpark notebook image is large (~7 GB). Remove it once you no longer need it to reclaim disk space:
docker rmi quay.io/okdp/jupyter/pyspark-notebook:spark-3.5.6-python-3.11-java-17-scala-2.13These images are published to quay.io/okdp/jupyter and consumed as the JupyterHub singleuser image by the OKDP sandbox. The Spark variants pull their tarballs from the OKDP Spark distribution instead of archive.apache.org.
| Symptom | Cause & fix |
|---|---|
bind: address already in use on 8888 |
Another process is bound to 8888; remap with -p 8889:8888 and open http://localhost:8889. |
Permission denied writing to a bind-mounted volume |
Host UID/GID differs from the container's jovyan user (1000:100). Run as root and let the entrypoint chown: -e NB_UID=$(id -u) -e NB_GID=$(id -g) -e CHOWN_HOME=yes --user root. See the upstream common features reference for all env vars. |
Spark driver OutOfMemoryError |
Default SPARK_OPTS caps the heap at -Xmx4096M. Override at run time: -e SPARK_OPTS="--driver-java-options=-Xmx8g". |
no space left on device during docker pull |
pyspark-notebook is ~7 GB. Free space with docker system prune, or first pull base-notebook to validate registry access. |
If you only need a JupyterLab image and are not committed to the OKDP stack, consider the following alternatives.
| Name | Description | When to choose |
|---|---|---|
| jupyter/docker-stacks | Upstream Jupyter community images that this repository is forked from. | You want the canonical upstream Dockerfiles without OKDP overrides, and you do not need the pre-built Spark integration wired to OKDP spark-tarballs. |
| Zero to JupyterHub on Kubernetes (Z2JH) | Official Helm chart for running a multi-user JupyterHub on Kubernetes. | You need a managed multi-user JupyterHub on Kubernetes and are happy to bring your own single-user image (which can still be this one). |
| The Littlest JupyterHub (TLJH) | Single-VM JupyterHub distribution targeted at small teams and classrooms. | You have fewer than ~100 users, want a single-server install, and do not need Kubernetes or Spark. |
| rocker/r-ver or python/python | Language-focused base images (R-centric Rocker stack, or plain Python) without a Jupyter UI. | You only need a language runtime for batch jobs or custom apps, and do not need JupyterLab at all. |
Pick OKDP/jupyterlab-docker when you are deploying as part of the OKDP data platform stack, want Spark integration matched to OKDP/spark-images, and need multi-arch (amd64/arm64) images out of the box.
Contributions follow the OKDP contribution guide. Released under the Apache License 2.0.
