JupyterHub for AI/ML: GPU quotas, MLflow integration, and team environments
Working on ML projects in a team often hits the wall of "it works on my machine but not on the server". Different library versions, no GPU access, unsynchronized datasets — chaos that kills productivity. JupyterHub with Kubernetes solves this at the infrastructure level. We have configured dozens of such environments for teams from 3 to 50 people and know all the pitfalls. One client — a company developing NLP models: 12 engineers used to spend up to 4 hours per week syncing environments. After implementing JupyterHub, that time dropped to zero. Turnkey — from image selection to MLflow integration — in 5 business days. We will assess your project for free, contact us.
Problems we solve
- Environment incompatibility. Each developer uses their own package versions. JupyterHub provides a unified Docker image with fixed dependencies. This eliminates "works on my machine" scenarios.
- GPU shortage. Without quotas, one user can hog all resources, leaving colleagues without compute. ResourceQuota and PriorityClass distribute GPUs fairly, and PriorityClass ensures production tasks are not preempted by research ones.
- Data duplication. Datasets are copied to each machine, wasting space and time on sync. A shared PVC in read-only mode solves this: the latest dataset version is available to all users at /data/shared.
Why JupyterHub over separate servers?
| Criterion | JupyterHub (Kubernetes) | Separate Servers |
|---|---|---|
| Reproducibility | Same image for all | Manual dependency installation |
| GPU management | Quotas, priorities, monitoring | No centralized control |
| Security | Isolation via Kubernetes Namespaces | Shared file access |
| Scaling | Autoscaling pods | Manual machine addition |
How we set up GPU quotas
For fair GPU distribution, we use ResourceQuota on namespaces and PriorityClass for task prioritization. Example configuration:
apiVersion: v1
kind: ResourceQuota
metadata:
name: jhub-quota
spec:
hard:
requests.nvidia.com/gpu: "8" # Max 8 GPUs at once
limits.memory: "512Gi"
requests.cpu: "64"
PriorityClass for GPU: research tasks have low priority, production inference has high priority. This prevents critical processes from being blocked.
How to integrate JupyterHub with MLflow?
Stack: Kubernetes (EKS/GKE), Helm, Docker, PyTorch 2.2, MLflow 2.11, DVC, Great Expectations.
# Add Helm repository
helm repo add jupyterhub https://hub.jupyter.org/helm-chart/
helm repo update
# config.yaml
cat > config.yaml << 'EOF'
hub:
config:
Authenticator:
admin_users:
- admin
GitHubOAuthenticator:
client_id: "your-github-client-id"
client_secret: "your-github-client-secret"
oauth_callback_url: "https://jupyter.company.com/hub/oauth_callback"
allowed_organizations:
- your-github-org
singleuser:
image:
name: jupyter/datascience-notebook
tag: "python-3.11"
profileList:
- display_name: "CPU Standard (4 CPU, 16GB RAM)"
description: "For EDA and light training"
default: true
- display_name: "GPU Instance (1x A100 40GB)"
description: "For model training"
kubespawner_override:
extra_resource_limits:
nvidia.com/gpu: "1"
- display_name: "GPU Large (2x A100 80GB)"
kubespawner_override:
extra_resource_limits:
nvidia.com/gpu: "2"
storage:
capacity: 50Gi
homeMountPath: /home/jovyan
# Shared storage for datasets (read-only for users)
singleuser:
extraVolumes:
- name: shared-datasets
persistentVolumeClaim:
claimName: shared-datasets-pvc
readOnly: true
extraVolumeMounts:
- name: shared-datasets
mountPath: /data/shared
readOnly: true
EOF
helm install jupyterhub jupyterhub/jupyterhub \
--namespace jhub --create-namespace \
--values config.yaml
For more details on configuration, see the official Zero to JupyterHub documentation.
Resource profiles detailed in the table:
| Profile | CPU | RAM | GPU | Storage |
|---|---|---|---|---|
| CPU Standard | 4 vCPU | 16 GB | – | 50 GB |
| GPU Instance | 4 vCPU | 32 GB | 1× A100 40GB | 100 GB |
| GPU Large | 8 vCPU | 64 GB | 2× A100 80GB | 200 GB |
Custom Docker images for ML
FROM jupyter/datascience-notebook:python-3.11
USER root
RUN apt-get update && apt-get install -y \
libgomp1 \
&& rm -rf /var/lib/apt/lists/*
USER ${NB_UID}
# ML dependencies
RUN pip install --no-cache-dir \
torch==2.2.0 torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 \
transformers==4.38.0 \
datasets \
accelerate \
peft \
mlflow==2.11.0 \
dvc[s3] \
great_expectations \
lightgbm xgboost catboost \
optuna \
shap \
wandb
# MLflow tracking server URL
ENV MLFLOW_TRACKING_URI=http://mlflow.internal:5000
# DVC remote config
COPY dvc_config /home/jovyan/.dvc/config
MLflow is automatically available from all notebooks via the environment variable. DVC is configured with corporate remote storage. The shared dataset folder with the latest dataset versions is mounted read-only. Git pre-commit hooks are installed globally for code standardization.
Typical result: An ML team of 10+ people works in a unified environment without "works on my machine" issues, with shared access to GPU resources and centralized experiment tracking. This significantly reduces compute costs through GPU utilization.
Process
- Analysis — study current infrastructure, GPU requirements, data volume.
- Design — develop architecture: namespaces, images, quotas.
- Implementation — deploy JupyterHub via Helm, configure authentication.
- Integration — connect MLflow, DVC, shared storage.
- Testing — verify scenarios: training launch, logging, data access.
- Team training — conduct a workshop, hand over documentation.
What's included
| Deliverable | Description |
|---|---|
| Infrastructure documentation | Architecture, deployment diagram, upgrade instructions |
| Configured cluster | JupyterHub with authentication, profiles, GPU quotas |
| Docker images | Ready-to-use images with PyTorch, MLflow, DVC |
| Integrations | MLflow, DVC, shared storage, pre-commit hooks |
| Training | 2-hour session for the team + recording |
| Support | 2 weeks after handover — bug fixes, answering questions |
How we maintain reproducibility?
We pin library versions in the Dockerfile, use lock files (pip freeze), and set up pre-commit hooks. The final environment is reproducible with a single helm install command. We guarantee stability for 2 weeks after deployment.
Guarantees and experience
Over 5 years of experience in MLOps, 20+ deployments for teams from 3 to 50 people. We guarantee 99.9% SLA availability and environment reproducibility. Order JupyterHub setup for your project — we will provide demo access to a working environment within 2 days. Get a consultation by contacting us.







