JupyterHub Basics
This page covers the fundamentals you need to work in a UZH JupyterHub deployment — from launching your server to managing files and Python environments.
The JupyterLab interface
JupyterLab is the web-based environment you see after logging in. It provides a file browser, notebook editor, terminal, and more.
Key areas of the UI:
| Area | Description |
|---|---|
| Left sidebar | File browser, running terminals & kernels, table of contents |
| Main work area | Open notebooks, scripts, terminals — arrange in tabs or splits |
| Status bar | Kernel name and status, line/column position |
| Launcher | Opens a new notebook, console, or terminal |
Using Git in JupyterLab
You can use Git directly from the terminal or via the built-in Git extension.
- JupyterLab Git extension docs
- Get started with Git (GitLab) — UZH’s official Git hosting is GitLab
Open a terminal from the Launcher and use standard git commands, or click the Git icon in the left sidebar for a visual interface.
Do not store credentials (or, in fact, anything really secret) on a JupyterHub server. Your team-mates (on a group-server), teachers, IT staff, will all have access to your files as one and the same user,
jovyan. We recommend short-lived github tokens.
Distributing materials with nbgitpuller
nbgitpuller is pre-installed in the standard course images. It lets teachers hand out a single click-to-open link that clones a Git repository into a participant’s home directory — and, on later clicks, merges updates without overwriting the participant’s own changes.
Generate a link with the nbgitpuller link generator:
| Field | What to enter |
|---|---|
| JupyterHub URL | The hub URL you reach once you are logged in (e.g. https://<course>.zi.uzh.ch) — not the OLAT/LMS link |
| Git repository URL | The HTTPS URL of the repository to pull (e.g. a GitLab or GitHub repo) |
| Branch | The branch to pull (optional; defaults to the repo’s default branch) |
| File to open | A notebook or folder to open after pulling (optional) |
Share the generated link with participants. Opening it logs them in (if needed), clones or updates the repository under /home/jovyan, and opens the requested file. See the nbgitpuller documentation for details on the underlying auto-merge behaviour.
👩🎓 Participants: If your teacher sends you an nbgitpuller link (it loads course materials into your home directory), you can simply click it — it logs you in if needed and copies the latest materials for you. Your own changes are preserved.
Python virtual environments and kernels
Each notebook runs against a kernel — a running Python process. The kernels available in the Launcher correspond to Python environments installed on your server.
What survives a server restart — and what doesn’t
| Location | Survives restart? |
|---|---|
Files and virtual environments under /home/jovyan | ✅ Yes — this is a persistent volume |
Packages you install with pip install (they go to ~/.local) | ✅ Yes — ~/.local lives under /home/jovyan |
| The pre-installed base environment itself | ♻️ Reset on restart — it lives in the container image and is rebuilt fresh |
On the UZH course images, pip is configured (via PIP_USER=1) so that pip install <package> installs into your personal ~/.local directory by default. Because ~/.local sits inside your persistent /home/jovyan, those packages survive restarts and are picked up by the base-environment kernel. In other words, pip install is the supported way to persistently extend the pre-configured base environment — you keep all the packages your course already provides and simply add your own on top.
The base environment image layer is still rebuilt fresh on every restart, so you can never permanently break it: if one of your ~/.local packages ever wedges things, removing it (or clearing ~/.local) returns you to a clean base.
When to create a dedicated virtual environment instead
Extending the base environment with pip install is convenient, but everything lands in one shared ~/.local. Create a dedicated virtual environment when you want:
- isolation — a self-contained set of packages that can’t clash with the base environment or with another project, or
- reproducibility — a clean, separately-registered kernel you can recreate from a
requirements.txt(see Sharing an environment definition).
Creating a custom kernel
The quickest way is the bundled new-venv-kernel helper. Open a terminal from the Launcher and run:
# Create a virtual environment and register it as a Jupyter kernel
new-venv-kernel myenv numpy pandas matplotlib # add whatever packages you need
This creates a virtual environment under ~/my-venvs/myenv, installs the packages you list (plus ipykernel), and registers a kernel named Python (myenv). Refresh the Launcher (or reload the page) — the new kernel will appear as an option.
Prefer to do it by hand? The helper is equivalent to:
# 1. Create a virtual environment in your home directory
python -m venv ~/my-venvs/myenv
# 2. Install your packages (ipykernel is required to register the kernel)
~/my-venvs/myenv/bin/pip install ipykernel numpy pandas matplotlib
# 3. Register the environment as a Jupyter kernel
~/my-venvs/myenv/bin/python -m ipykernel install --user --name myenv --display-name "Python (myenv)"
Where does the environment live?
The virtual environment is created under~/my-venvs/myenv, which is inside/home/jovyan— your persistent home directory. The kernel registration is stored under~/.local/share/jupyter/kernels/myenv, also inside/home/jovyan. Both survive server restarts.
Sharing an environment definition
To make your environment reproducible, export its package list to a file:
~/my-venvs/myenv/bin/pip freeze > requirements.txt
Anyone with that file — including your future self after losing an environment — can recreate it with:
new-venv-kernel myenv -r requirements.txt
or by hand:
python -m venv ~/my-venvs/myenv
~/my-venvs/myenv/bin/pip install ipykernel -r requirements.txt
~/my-venvs/myenv/bin/python -m ipykernel install --user --name myenv --display-name "Python (myenv)"
👩🎓 Participants: Course environments are pre-configured by your teacher. To add an extra package,
pip install <package>is usually enough — on the course images it installs into your persistent~/.localand extends the base environment (see What survives a restart). Create a dedicated virtual environment (as shown above) when you need an isolated, reproducible setup.
👩🏫 Owners / Coaches: Pre-installed kernels are baked into the server Docker image, which is built and deployed by ZI. To add a kernel or package for the whole course, contact ZI so it can be added to the course image. You can also provide a
requirements.txtto participants so they can recreate the exact environment in their own server.
UZH persistent volumes
Each JupyterHub deployment provides two persistent storage areas:
/home/jovyan— your personal home directory. Notebooks, data files, and custom virtual environments stored here survive server restarts./home/jovyan/shared— a read-only directory (symbolic link to/home/shared) shared with the whole course.
Both volumes are backed by Azure Blob Storage over NFS, which means they are network-attached and not limited by the number of disks a virtual machine can hold. This allows many students to run concurrently on the same node.
Key points:
/home/jovyan— your personal persistent directory. Files here are not lost when your server stops. Each user’s data is stored in a separate subdirectory on the shared volume, isolated by username. It is private — other participants cannot see it./home/jovyan/shared— read-only course materials provided by your teacher. It is not a place to exchange files with classmates: you cannot write to it./tmp— temporary storage, cleared on restart. Do not store important work here.
Sharing files within a team? The
shareddirectory is read-only and only your teacher can put files there, so it cannot be used to exchange files with team-mates. To work on the same files together, ask your teacher to put your team on a collaboration group server, where all group members share one home directory. See Roles, Groups & Collaboration.
👩🎓 Participants: Always save your work under
/home/jovyan. If you need to download large datasets temporarily, use/tmpto avoid filling your home directory. If you run out of space, contact your course administrator.To bring your own files in, drag them from your computer onto the file browser in the left sidebar of JupyterLab (or use its upload button). They are stored under
/home/jovyanand survive restarts.
👩🏫 Owners / Coaches: User home directories are served by a single shared Blob NFS PersistentVolumeClaim (
blob-homes-pvc) with per-user subdirectories. There is no per-user storage quota at the Kubernetes level — monitoring storage usage should be done at the Azure storage account level.Writing to the shared volume (
/home/shared) is possible by uploading to the shared-materials container (jupyterhub) in the Azure storage account — this is a different container from the one holding user home directories (jupyterhub-homes). A usage guide for this will be provided to you by ZI.