This tutorial shows how to deploy nbgrader on Jetstream Kubernetes using a shared Manila disk mounted on all JupyterHub user pods.
This is for the setup where:
- You already created a Magnum cluster named
k8s. - You already created a Jetstream-managed Manila share named
nbgraderexchange(via Exosphere). - You want nbgrader’s default filesystem exchange (no
ngshare).
We will:
- Check out this repository locally (contains all config files used below).
- Configure
kubectlfor the Magnum cluster. - Mount the existing Manila share into all JupyterHub user pods at
/share. - Configure nbgrader to use
/share/nbgrader/exchange. - Run the full instructor/student workflow (
release,fetch,submit,collect,autograde).
If you want the API-based approach instead of a shared disk, see the previous tutorial: ./2026-02-04-nbgrader-ngshare-jetstream.md
Difference in one line: this tutorial uses nbgrader’s native filesystem exchange on a Manila RWX share, while the previous tutorial uses ngshare (REST API exchange without shared filesystem).
Why this approach
nbgrader’s native exchange expects a shared filesystem visible from both instructor and student pods.
A Manila share mounted as ReadWriteMany provides exactly that.
Prerequisites
- A running Magnum cluster named
k8s. - JupyterHub deployed with Helm.
- A Manila share already created (or created in Step 2 below).
- OpenStack credentials file (
*openrc*.sh) available. - A shell environment where required CLIs are installed and configured:
openstack,kubectl, andhelm.
Step 1: Check out this repository
This tutorial uses config files from this repo (manila/, nbgrader/, config_*.yaml, etc.), so clone it first:
git clone https://github.com/zonca/jupyterhub-deploy-kubernetes-jetstream.git
cd jupyterhub-deploy-kubernetes-jetstreamStep 3: Configure access to the Magnum cluster
From the repo root:
# Ensure your current shell already has OpenStack credentials loaded
# and has openstack/kubectl/helm available.
export K8S_CLUSTER_NAME=k8s
bash kubernetes_magnum/configure_kubectl_locally.sh
export KUBECONFIG=$(pwd)/config
kubectl get nodesYou should see nodes from your k8s cluster.
Step 6: Install nbgrader and configure filesystem exchange
Use this file from the repo:
nbgrader/jhub-singleuser-nbgrader-filesystem.yaml
Replace COURSE_ID with your course ID (example: course101).
Add this file to install_jhub.sh:
--values nbgrader/jhub-singleuser-nbgrader-filesystem.yaml \
Re-deploy:
bash install_jhub.shThen stop and start existing user servers so the postStart hook re-runs and writes the updated nbgrader_config.py.
Step 8: Instructor workflow (create + release)
From this point, the nbgrader workflow is the same as in the previous tutorial:
- Create/release:
./2026-02-04-nbgrader-ngshare-jetstream.md#step-6-create-and-release-a-first-assignment - Student fetch/submit:
./2026-02-04-nbgrader-ngshare-jetstream.md#step-7-student-workflow-fetch--submit - Instructor collect/autograde:
./2026-02-04-nbgrader-ngshare-jetstream.md#step-8-instructor-workflow-collect--autograde
Manila-specific difference:
- You do not need
ngshare-course-management. - Student roster is managed with
nbgrader db student add ...on the instructor side.
Step 9: Student workflow (list + fetch + submit)
Use the same commands as linked in Step 8.
Step 10: Instructor workflow (collect + autograde)
Use the same commands as linked in Step 8.
Notes
- This tutorial uses nbgrader’s filesystem exchange on a shared Manila disk.
- No
ngshareservice orngshare_exchangepackage is required. - The important part is that all user pods mount the same RWX path and permissions allow read/write for instructor and students.
Troubleshooting
If nbgrader fetch_assignment or submit fails with permission errors:
chmod -R 0777 /share/nbgrader/exchangeIf user pods don’t see /share, confirm both values files were included in install_jhub.sh and run bash install_jhub.sh again.
If the Manila PVC is not Bound, re-check placeholders in:
manila/cephfs-csi-values.yamlmanila/cephfs-csi-pv.yaml
If pods are stuck in ContainerCreating with Ceph mount errors:
missing required field fsName: addfsName: "cephfs"inmanila/cephfs-csi-pv.yaml.mount error 3 = No such process:rootPathis wrong; use only/volumes/...path.failed to find the secret csi-cephfs-secret in namespace default: alignnodeStageSecretRef.namespacewith the namespace where the secret exists.