Skip to main content
  1. Posts/

Creating My Own `JupyterLab` Workspace with Docker

Table of Contents
This is a short TID about setting up a JupyterLab workspace with Docker. 🤔

Why I started #

I wanted a small place to test short pieces of Python and JavaScript without installing every dependency directly on my machine. JupyterLab was a good fit because I could run both languages from a browser-based workspace.

Docker keeps the runtime and its dependencies inside the workspace. The same setup can run on different operating systems, as long as Docker is available.

This setup is for a local or trusted network. The example disables the Jupyter token for convenience, so it should not be exposed to the public internet without adding proper authentication.

The workspace structure #

I kept the setup small and separated the container definition, server settings, and notebooks.

.
├── Dockerfile
├── docker-compose.yml
├── requirements.txt
├── config
│   └── jupyter_server_config.py
└── workspace
    ├── python
    └── javascript

Dockerfile builds the image, docker-compose.yml runs the service, and config/jupyter_server_config.py contains the Jupyter Server settings. The workspace directory stays on the host through a volume mount.

Building the image #

I used the jupyter/docker-stacks base-notebook image as the starting point. The first stage installs Python packages, and the ijs stage adds the iJavaScript kernel.

FROM jupyter/base-notebook:ubuntu-22.04 AS base

COPY --chown=${NB_UID}:${NB_GID} requirements.txt /tmp/
RUN pip install --quiet --no-cache-dir --requirement /tmp/requirements.txt && \
    fix-permissions "${CONDA_DIR}" && \
    fix-permissions "/home/${NB_USER}"

FROM base AS ijs

RUN npm install -g ijavascript
RUN ijsinstall

Using a separate image target makes the extra JavaScript kernel explicit. If I only need Python, I can stop at the base image and avoid installing iJavaScript.

Connecting the container #

The Compose file mounts the workspace and server config, then exposes JupyterLab on port 8888.

services:
  jupyter:
    container_name: jupyter
    build:
      context: .
      dockerfile: Dockerfile
      target: ijs
    volumes:
      - ./workspace:/home/jovyan/work
      - ./config/jupyter_server_config.py:/etc/jupyter/jupyter_server_config.py
    ports:
      - "8888:8888"
    command: jupyter lab --allow-root --ServerApp.token=''

The volume is the important part for this workspace. Rebuilding the container does not remove the notebooks stored under workspace.

Server settings #

The config file lets the server accept connections from the container network and prevents it from opening a browser inside the container.

c = get_config()  # noqa: F821
c.ServerApp.ip = "0.0.0.0"
c.ServerApp.open_browser = False

# Required for the VS Code Jupyter extension in this setup.
c.ServerApp.disable_check_xsrf = True

The full example came from the base-notebook server configuration. Disabling XSRF checks and the token is not a default I would use for a public server.

Running it #

Once the files are ready, I can build and start the service with Docker Compose.

docker compose up -d
docker compose logs -f

When the container is running, I can open http://localhost:8888/lab in a browser. The Python and iJavaScript kernels are then available from the same workspace.

What I learned #

The useful part was not only running JupyterLab in Docker. Keeping the image, server settings, and host workspace separate made the setup easier to rebuild without losing notebooks.

The no-token option is convenient for local experiments, but it also makes the boundary of the setup clear. If I deploy this somewhere reachable by other people, authentication and a safer server configuration need to come first.

References #