JupyterHub services (not to be confused with Kubernetes Service objects) are processes that interact with the JupyterHub API. nbgrader and culling idle Notebooks are examples of production services, and there are minimal examples of “hello world” services in the Jupyterhub examples repo.

Services can be run externally from the Hub, meaning they are started and stopped independently of the Hub and must know about things like their Hub authentication token on their own. Alternatively, a service can be Hub-managed, where the Hub starts and stops the process and passes key information to the service via environment variables.

Hub-managed services in z2jh#

A Hub-managed service will run in the same container/pod as the Hub itself. First, you’ll need to install or copy the appropriate files for the service into your Hub image, either by creating a custom image derived from jupyterhub/k8s-hub or the hub.extraFiles configuration. Keep in mind that your Hub container may need to install dependency libraries like flask or fastapi, depending on the service. In those cases, you’ll need a custom image.

In addition to the code for the service, you need to modify the Hub Kubernetes Service object to include multiple ports, and update the Hub Network Policy. If you want to allow access from all sources, you can use hub.networkPolicy.allowedIngressPorts. Otherwise if you want to more precisely control access, you can use hub.networkPolicy.ingress.

Example service#

In the following snippet, I’m using a custom image that copies over the application code and installs the dependencies listed in the fastapi service example.

# Dockerfile
# 2.0.0 is latest stable release at the time of this writing
FROM jupyterhub/k8s-hub:2.0.0

# Depending on version, the k8s-hub image may have installed
# pip packages as root, forcing you to install as root as well
USER root
COPY ./service-fastapi /usr/src/fastapi
RUN python3 -m pip install -r /usr/src/fastapi/requirements.txt

# config.yaml

    name: myregistry/my-custom-hub-image
    tag: latest

      url: http://hub:8181
        - /home/jovyan/.local/bin/uvicorn
        - app:app
        - --port
        - "8181"
        - --host
        - ""
        - --app-dir
        - /usr/src/fastapi

      - ports:
          - port: 8181
          - podSelector:

      - port: 8181
        targetPort: 8181
        name: fastapi