# JupyterHub notebook server

Source: https://docs.quake.ai/resources/iac-templates/jupyterhub
Markdown: https://docs.quake.ai/resources/iac-templates/jupyterhub.md

---

# JupyterHub notebook server

This pattern composes Compute, Network, and Block Storage into a self-hosted multi-user notebook server you run on infrastructure you control.

## What this template does

Provisions a single instance running [JupyterHub](https://jupyterhub.readthedocs.io), a multi-user notebook server (a self-hosted alternative to Google Colab or Deepnote at the CPU tier). Each user gets a CPU-only scipy notebook container with persistent storage:

- Compute instance that runs JupyterHub in Docker with DockerSpawner, sized for the hub plus one concurrent scipy notebook (4 vCPU and 4 GiB RAM)
- Private network, subnet, router, port, and security group; a floating IP for public access
- A block volume mounted at `/var/lib/docker`, so the hub database and per-user notebook volumes live on a volume you can grow rather than on the boot disk
- cloud-init installs Docker Engine, builds the hub image, and starts JupyterHub from a compose file on first boot

JupyterHub is the analyst and data-scientist front door for notebooks on the platform. It runs multi-user notebooks on a VM you own, which keeps hub state and user data on your infrastructure.

No credential ships with this template. You register the admin account through JupyterHub's signup page on first visit, and each additional user registers through the same flow.

## Parameters

| Parameter | Description | Default |
| --- | --- | --- |
| `key_name` | SSH keypair name (must already exist) | No default |
| `flavor_name` | Instance size (hub plus one scipy notebook on 4 vCPU / 4 GiB) | `s1a.medium` |
| `image_name` | Operating system image | `Ubuntu-24.04` |
| `app_name` | Display name prefix for resources | `jupyterhub` |
| `volume_size` | Block volume size in GiB, mounted at `/var/lib/docker` | `30` |
| `external_network` | External network for floating IP allocation | `PublicStatic` |
| `private_cidr` | CIDR for the private subnet | `10.40.0.0/24` |
| `hub_allowed_cidr` | CIDR allowed to reach the hub on port 8000 | `10.40.0.0/24` |
| `notebook_image` | CPU-only Docker image for per-user notebook servers | `quay.io/jupyter/scipy-notebook:2024-10-14` |

## Hub access and security

The hub listens on port 8000 over plain HTTP. The security group restricts 8000 to `hub_allowed_cidr`, which defaults to the private network only, so the raw hub stays off the public internet. JupyterHub's NativeAuthenticator gates access through its signup page. Reach the hub one of three ways:

- Put a reverse proxy (Caddy or Nginx) in front of JupyterHub and serve the hub over HTTPS on 443. Point the domain's DNS A record at the floating IP. This is the recommended path for routine access.
- Tunnel over SSH: `ssh -L 8000:localhost:8000 ubuntu@FLOATING_IP`, then open `http://localhost:8000`.
- Set `hub_allowed_cidr` to `YOUR_IP/32` to reach port 8000 directly from one address.

Ports 80 and 443 stay open for the reverse proxy you put in front; they carry no traffic until you add one.


Per-user notebook volumes and the hub database live on the data volume under `/var/lib/docker`. Snapshot the volume before you resize or rebuild the host.


## CPU-only boundary

This template spawns CPU-only scipy notebook containers. It does not provision GPUs, CUDA drivers, or GPU-backed notebook images. For GPU inference, RAG pipelines, or model serving, use the [inference gateway template](/resources/iac-templates/inference-gateway) or the [AI inference and RAG deployment walkthrough](/resources/deployments/deploy-inference-gateway-template).

## When to use this pattern

Run a multi-user notebook server for analysts and data scientists on a VM you operate. JupyterHub suits exploratory analysis, shared CPU notebooks, and teaching environments where each user needs an isolated scipy stack.

For a single-user notebook or a lightweight data app, consider [Streamlit](/resources/iac-templates/streamlit) or a [dev environment](/resources/iac-templates/dev-environment). For the warehouse and experiment-tracking layers that pair with notebooks, see [self-managed PostgreSQL](/resources/iac-templates/self-managed-postgres) and the Notebook workbench architecture brief.

## Estimated cost

<PricingCompanion
  components={[
    { kind: "template", slug: "jupyterhub", required: true },
  ]}
/>

## Template source

<TemplateSource slug="jupyterhub" />

<TemplateResourceMap template="jupyterhub" format="opentofu" />

## Customize this pattern

- [Customize a template's image and flavor](/docs/automation/how-to/customize-template-image-flavor)
- [Add a block volume to a template](/docs/automation/how-to/add-volume-to-template)
- [Parameterize a template with a tfvars file](/docs/automation/how-to/parameterize-template-tfvars)

## See also

- [self-managed PostgreSQL](/resources/iac-templates/self-managed-postgres)
- [S3-compatible object storage](/resources/iac-templates/s3-storage-acl)
- [inference gateway](/resources/iac-templates/inference-gateway)
