Self-hosted service
jupyterhub/jupyterhub avatar
jupyterhub/jupyterhub

JupyterHub: a multi-user Hub for Jupyter notebook servers

Multi-user server for Jupyter notebooks

8,345 stars2,117 forksPythonBSD-3-Clause

At a glance

What is it?
JupyterHub puts a login and a proxy in front of many single-user notebook servers. It is aimed at classes, research groups and HPC clusters, and it assumes you can run a Linux service, not just a laptop.
Who is it for?
Adopt JupyterHub when several people need their own notebook server behind one login and you can run a Linux service with a domain and TLS certificate. Do not adopt it for a single user, or if you cannot run the Hub as a privileged user or configure sudo-based spawning.
Can I use it commercially?
Yes. BSD-3-Clause 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 1 day ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

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

Editorial analysis

What JupyterHub adds on top of a single notebook server

A single-user Jupyter notebook server is one process serving one person. JupyterHub exists because that model breaks the moment a second person needs access. The README states that the project was created by Project Jupyter "to support many users", and names four settings it was built for: a class of students, a corporate data science workgroup, a scientific research project, and a high-performance computing group. Those four share a property. The users are known to an institution, they arrive and leave in cohorts, and nobody wants to hand each of them a machine.

The Hub itself is not a notebook environment. It is a control plane. Each user gets their own single-user Jupyter notebook server, spawned on demand, and the Hub decides who that user is and where their server lives. That separation is the whole point: the notebook process stays a normal Jupyter server, and the multi-user concerns (login, routing, lifecycle) sit outside it.

This is a server product for administrators, not a desktop tool. The pyproject.toml classifiers list "Intended Audience :: System Administrators" alongside developers and researchers, and the operating system classifier is POSIX only. setup.py prints "WARNING: Windows is not officially supported" when it detects a Windows build. If you want a notebook on your laptop, you want JupyterLab, not this.

Hub, proxy and single-user servers: the three actors

The README describes three actors: the multi-user Hub, a configurable HTTP proxy (node-http-proxy), and the single-user Jupyter notebook servers. The operating sequence is short enough to quote in full: the Hub launches a proxy; the proxy forwards all requests to the Hub by default; the Hub handles login and spawns single-user servers on demand; the Hub configures the proxy to forward URL prefixes to those single-user servers.

Read that sequence carefully, because it explains most operational behaviour. The proxy starts knowing only about the Hub. Every request lands on the Hub first. A user who is not yet authenticated never reaches a notebook process. Once the Hub has authenticated someone and started their server, it rewrites the proxy's routing table so that a URL prefix points at that specific server. Traffic then flows proxy to notebook directly, without the Hub in the path.

The Hub is a Tornado process, and the single-user servers are described as Python/Jupyter/tornado. The proxy is Node. So a working deployment has at least two runtimes on the machine, which is why the prerequisites ask for nodejs/npm even when you install the Python side with pip. The repository's package.json names the front-end dependencies the Hub's own pages use: bootstrap, jquery, moment, requirejs and @fortawesome/fontawesome-free.

There is also a REST API, linked from the README as the interface "for administration of the Hub and its users". The examples directory shows what that API is used for in practice: examples/server-api, examples/service-fastapi, examples/service-whoami and examples/service-whoami-flask are all services that talk to the Hub, and examples/custom-scopes suggests the API is scope-based rather than all-or-nothing.

Installing JupyterHub and signing in for the first time

The prerequisites are explicit: a Linux/Unix based system, Python 3.10 or greater, nodejs/npm (installed for you by conda, or at least version 12.0 if you use pip), PAM if you keep the default authenticator, plus a TLS certificate and key and a domain name. The domain and certificate matter because the README's own example starts the Hub over HTTPS.

With conda, one command installs JupyterHub and its nodejs/npm dependencies. Run it inside the environment you intend to use.

bash
conda install -c conda-forge jupyterhub

With pip, the Python package and the Node proxy are separate installs. This is the step people miss; without configurable-http-proxy on the PATH, the Hub has no proxy to launch.

bash
npm install -g configurable-http-proxy
python3 -m pip install jupyterhub

If the same machine will host notebook servers locally, install a notebook front end. The README lists JupyterLab and the classic notebook as the two options.

bash
python3 -m pip install --upgrade jupyterlab
python3 -m pip install --upgrade notebook

Starting the Hub is one command. The README says to visit http://localhost:8000 and sign in with your system username and password, which is the PAM authenticator doing its work.

bash
jupyterhub

For anything beyond a local trial, generate a configuration file rather than passing flags. The README gives the generator command, and the resulting file carries settings with descriptions.

bash
jupyterhub --generate-config

Then start the Hub on a specific address and port with TLS. The README uses 10.0.1.2:443 with my_ssl.key and my_ssl.cert as the example values.

bash
jupyterhub --ip 10.0.1.2 --port 443 --ssl-key my_ssl.key --ssl-cert my_ssl.cert

One warning comes with the first-run instructions. To let multiple users sign in, the README says you need to run the jupyterhub command as a privileged user, such as root. The documentation describes running it as a less privileged user instead, and the README is honest that this "requires more configuration of the system". That trade-off is the first real decision in any deployment.

Authenticators and spawners are where the deployment gets specific

JupyterHub ships with defaults that work for a small, trusted group and stops being sufficient almost immediately after that. The built-in PAMAuthenticator checks system usernames and passwords. The built-in LocalProcessSpawner starts single-user servers as local processes. Together they mean every Hub user must also be a local account on the host, and every notebook runs as a process on that same host.

The README points to replacements. For authentication: OAuthenticator for OAuth, ldapauthenticator for LDAP, kerberosauthenticator for Kerberos. For spawning: dockerspawner for containers, kubespawner for Kubernetes, sudospawner to spawn without being root, systemdspawner to use systemd. Note that all of these are separate projects, not modules inside this repository. Choosing them means tracking several release schedules, and a compatibility break in any one of them lands on you.

The examples directory shows the same pattern for other concerns. examples/postgres shows a database-backed deployment, examples/cull-idle addresses idle servers, examples/read-only and examples/user-sharing cover access patterns, examples/azuread-with-group-management and examples/external-oauth cover identity integration, and examples/collaboration-accounts covers shared accounts. None of these are documented in the README beyond their directory names, so treat the examples as starting points to read, not as supported configurations.

The sudospawner entry deserves attention because it is the answer to the privilege problem from the previous section. Running the Hub as root is the default path; sudospawner exists specifically to spawn single-user servers without being root. If you cannot run a root service, that is the package to evaluate first.

Where JupyterHub is the wrong tool

The clearest failure mode is scope. JupyterHub does not give one person a better notebook. It gives many people separate notebooks. If you are a solo researcher who wants a nicer editor, JupyterLab is the thing the README tells you to install alongside the Hub, and installing it alone skips the proxy, the PAM dependency, the domain and the TLS certificate entirely.

The second limitation is the privilege requirement. The README states plainly that multiple users signing in requires running the jupyterhub command as a privileged user such as root. The alternative, documented separately, needs more system configuration. On a shared machine where you do not control the service user, this is a blocker rather than an inconvenience, and sudospawner or a container-based spawner becomes mandatory rather than optional.

The third is the platform boundary. POSIX only, with an explicit unsupported warning for Windows. If your users are on Windows desktops, JupyterHub is a server they reach over the network, not something you install for them.

Finally, the README does not document rollback, backup or upgrade procedures for a running Hub. RELEASE.md exists in the repository, and the docs directory is the place those procedures would live, but nothing in the README describes what happens to running user servers when the Hub is upgraded or restarted. Plan for that gap before you put a term's worth of student work behind it.

JupyterHub compared with plain JupyterLab and with Kubernetes-native deployment

The comparison people actually search for is JupyterHub versus JupyterLab, and the honest answer is that they are not alternatives. JupyterLab is the notebook interface a single user works in. JupyterHub is the layer that decides which JupyterLab instance you get and how you reach it. The README lists JupyterLab as a package to install when you want notebook servers running locally under the Hub. You can run JupyterLab without JupyterHub; you cannot run JupyterHub without some notebook front end for users to land in.

The more interesting comparison is between the LocalProcessSpawner default and the Kubernetes route. LocalProcessSpawner starts single-user servers as local processes on the Hub host, which makes the host the unit of capacity: memory, CPU and disk are shared, and one runaway notebook affects everyone. kubespawner is a separate project listed in the README that spawns each user's server as a Kubernetes pod, which moves isolation and resource limits to the cluster. The cost is that you now operate Kubernetes. The examples directory does not include a Kubernetes manifest, so that path leads out of this repository and into kubespawner's own documentation.

There is also a middle option in dockerspawner, which spawns single-user servers in Docker containers on the Hub host. It keeps the single-machine model but gives each user a container boundary. Between the three, the choice is about how much isolation you need and how much infrastructure you are prepared to run.

Licence, maintenance and the cost of keeping a Hub current

JupyterHub is BSD-3-Clause, stated in both pyproject.toml and package.json, with the LICENSE file at the repository root and license-files declaring it in the build metadata. That is a permissive licence, and it applies to this repository. The authenticators and spawners the README recommends are separate projects with their own licences, so a full deployment is a licence review of several packages, not one. Nothing here is legal advice; check each dependency's licence against your organisation's policy.

The repository is not archived, and the last push was on 2026-09-21. The version in pyproject.toml is 6.0.2.dev, and the classifiers include "Development Status :: 5 - Production/Stable". The Python floor is 3.10, and requirements.txt pins the runtime surface: tornado 6.5 or later, SQLAlchemy 1.4.1 or later, alembic 1.4 or later, traitlets 5.4 or later, jupyter_events 0.11.0 or later, pydantic 2 or later, plus pamela on non-Windows platforms and psutil on Windows. Those floors are the upgrade cost. A Hub that has been running for a year is likely several minor versions behind on tornado, SQLAlchemy and pydantic, and the alembic dependency means the Hub has database migrations to run.

Upgrading is therefore not a pip install away from being safe. The repository has a noxfile.py and a ci/ directory including ci/oldest-dependencies, which indicates the project tests against old dependency versions deliberately, but that is about the project's own compatibility testing, not a guarantee about your deployment. The README does not describe an upgrade procedure for a live Hub.

Editorial conclusion

Adopt JupyterHub when several people need their own notebook server behind one login and you can run a Linux service with a domain and TLS certificate. Do not adopt it for a single user, or if you cannot run the Hub as a privileged user or configure sudo-based spawning. Before committing, verify that your Python is 3.10 or greater, that nodejs/npm is at least 12.0 if you install with pip, and that your chosen authenticator and spawner are separate packages you are willing to maintain.

Frequently asked questions

What is JupyterHub used for?

It creates a multi-user Hub that spawns, manages and proxies multiple instances of the single-user Jupyter notebook server. The README names classes of students, corporate data science workgroups, scientific research projects and high-performance computing groups as the settings it was built for.

What is the difference between Jupyter Notebook and JupyterHub?

A single-user Jupyter notebook server serves one person; JupyterHub is the layer that handles login and routes many users to their own such servers. The README lists JupyterLab and notebook as packages to install alongside the Hub when you want notebook servers running locally.

How do I install JupyterHub?

With conda, one command installs JupyterHub and its nodejs/npm dependencies. With pip, you install configurable-http-proxy through npm and then install the jupyterhub Python package, and you need Python 3.10 or greater.

Is JupyterHub free?

Yes. The repository is licensed BSD-3-Clause, stated in pyproject.toml and package.json, and the package is distributed through PyPI and conda-forge. The authenticators and spawners the README recommends are separate projects with their own licences.

How do I access JupyterHub after starting it?

The README says to run the jupyterhub command and visit http://localhost:8000 in a browser, then sign in with your system username and password. To let more than one user sign in, the README states the command must be run as a privileged user such as root.

How do I set up JupyterHub?

Generate a configuration file with jupyterhub --generate-config, then start the Hub with the settings you need, for example --ip 10.0.1.2 --port 443 --ssl-key my_ssl.key --ssl-cert my_ssl.cert for HTTPS. The README points to the Getting Started section of the documentation for the common setup steps.

Official sources

  1. Issues
  2. jupyterhub/jupyterhub on GitHub
  3. License: BSD-3-Clause
  4. Project website
  5. README
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/jupyterhub-jupyterhub.svg)](https://hysenlabs.com/projects/jupyterhub-jupyterhub)