Notebooks Stack (JupyterHub)
Reference for SeaGit’s Notebooks template: a multi-user JupyterHub in a Kubernetes cluster in your own AWS account. Each developer signs in and gets a private JupyterLab with a persistent home and keyless access to an S3 bucket.
What the Notebooks Stack is
The Notebooks template deploys JupyterHub 5 into a cluster you choose, from a single form. A developer opens the hub’s address, signs in, picks a server size and an image, and works in JupyterLab in the browser or from VS Code. Their home folder is kept between sessions. A server that sits idle stops itself, so you pay for compute only while someone is working. Notebooks read and write one S3 bucket through an IAM role, with no access keys to hand out.
It is built for a team of roughly 10 to 50 people sharing one hub. In a live run the hub was ready about seven minutes after Deploy on a cluster that already existed, most of it the load balancer warming up; a developer’s first server took about two and a half minutes to start.
The template is available on paid plans. It needs a native (sgt-k8s) cluster with three add-ons: EBS CSI (home volumes), Load Balancer (the hub’s own load balancer) and cert-manager (the HTTPS certificate), and an environment with a domain. The wizard tells you which add-on is missing on the cluster you pick.
What gets deployed
One application with one component, hub: a release of the sg-notebooks Helm chart, which wraps the upstream Zero to JupyterHub chart, pinned to one cluster. Nothing cluster-wide is installed.
| Part | What it is |
|---|---|
| Hub | One JupyterHub pod. It signs people in, starts and stops their servers, and keeps its own small database on an EBS volume. |
| Proxy and load balancer | A dedicated internet-facing Network Load Balancer in front of the hub’s proxy. HTTPS on 443; plain HTTP is redirected. Only the Allowed IPs can connect. |
| Name and certificate | <dep>.<environment domain>, with a Let’s Encrypt certificate from cert-manager. A renewed certificate is loaded automatically. |
| A server per developer | A private JupyterLab pod, started on sign-in and stopped when idle. Each has its own home volume. |
| Data bucket | An S3 bucket (or a folder of yours) in the cluster’s AWS account. Servers reach it through an IAM role; there are no access keys. |
Deploy it
- Open Applications → Templates and choose Notebooks. Clicking the card opens its details; Deploy starts the wizard.
- First step: name, environment, cluster and space, then the sign-in method and the data bucket (new, or a folder of a bucket you own).
- Second step: who may sign in, the sizes and images developers can pick, home storage, the idle timeout and the Allowed IPs.
- Review and deploy. When the stack is ready, the Stack tab shows the hub address, the sign-in details and code to copy.
Sign-in
| Method | What you provide | Good for |
|---|---|---|
| Shared passwords | One admin password and one team password, generated per stack, plus a list of usernames that may sign in. | Trials and small teams. Nothing to set up outside SeaGit. |
| GitHub | A GitHub OAuth app. Access by organization (acme) or team (acme:data-science). | Teams that already manage membership in GitHub. |
| Single sign-on (OpenID Connect) | Okta, Entra ID, Google, Keycloak, Cognito or any OIDC provider, by issuer URL. Optional allowed groups and admin groups. | Companies with an identity provider. Offboarding happens there. |
Sign-in is handled by the hub itself. You do not need a separate OAuth gateway in front of it.
Shared passwords
The Stack tab shows the admin and team passwords masked. Each reveal needs update access to the app and is written to the audit log. Admins sign in with the admin password; the usernames you listed sign in with the team password. Anyone else is refused, with either password.
GitHub and single sign-on
Create the OAuth app or client with your provider, paste its client ID and secret into the form, and deploy. The secret is stored encrypted and is not shown again. Then copy the OAuth callback URL from the Stack tab into the provider; the address is only known after the first deploy. For single sign-on, the endpoints are read from the issuer’s discovery document. To restrict access by group, set Allowed groups; Okta sends the groups claim only when groups is added under Extra scopes.
Admins
Admins see every server in the hub’s admin page and can stop them. With GitHub they are GitHub usernames; with single sign-on, the username claim (usually the email) or members of the Admin groups.
Servers
Sizes
You choose which sizes developers can pick. The limit is what a server may use; the guaranteed memory is what must be free on one node for the server to start.
| Size | Limit | Guaranteed memory |
|---|---|---|
| X-Small | 0.5 vCPU, 2 GB | 512 MB |
| Small | 1 vCPU, 4 GB | 1 GB |
| Medium | 2 vCPU, 8 GB | 2 GB |
| Large | 4 vCPU, 16 GB | 4 GB |
| X-Large | 8 vCPU, 32 GB | 8 GB |
Check the guaranteed memory against your nodes. On a pool of 2 GB nodes a Small server may not fit beside the system pods; offer X-Small there or add a larger pool.
Images
| Image | What it is |
|---|---|
| Python data science | quay.io/jupyter/scipy-notebook: pandas, NumPy, SciPy, scikit-learn, Matplotlib. |
| PyTorch | quay.io/jupyter/pytorch-notebook: the above plus PyTorch. |
| Python, R and Julia | quay.io/jupyter/datascience-notebook: three kernels in one image. |
| Your team image | Any image you build FROM a quay.io/jupyter image, given by full reference (for example an ECR image in the cluster’s account). |
The built-in images are pinned to a dated tag, so every developer gets the same versions. Packages installed with pip into the home folder survive a restart; packages installed system-wide do not. For a stable team environment, build an image.
Home storage
Each developer gets an EBS volume (10 GiB by default) mounted as the home folder, created on first start. It is kept when the server stops and deleted when the stack is deleted. Put data you want to outlive the stack in the bucket.
Idle servers
A server with no activity for the chosen time (30 minutes to 24 hours, 1 hour by default) is stopped. The home is kept, and the server starts again at the next sign-in. A kernel that is busy counts as activity.
Spot nodes
Servers stay off spot nodes by default. If you allow spot, a reclaimed node stops the servers on it; homes are kept, unsaved kernel state is lost.
Data bucket
| Choice | What happens |
|---|---|
| Create new bucket | SeaGit creates a bucket in the cluster’s AWS account. Leave the name empty for a generated one. Encryption: S3-managed keys, a new KMS key SeaGit creates (you choose its alias), or a KMS key you already have. |
| Use a folder of an existing bucket | A bucket you already own, in the same account, by name or ARN. The bucket is required; the folder defaults to seagit-notebooks/<app name>/. Servers can read and write that folder only. Optionally a KMS key ARN; empty uses the bucket’s default encryption. |
Every server runs under one Kubernetes service account tied to an IAM role for that bucket or folder, so boto3, s3fs, pandas and the AWS CLI work without keys. These variables are set in every server:
| Variable | Value |
|---|---|
| SEAGIT_BUCKET | The bucket name |
| SEAGIT_PREFIX | The folder inside it (empty for a new bucket) |
| SEAGIT_BUCKET_URI | s3://<bucket>/<folder>, ready to prefix a path |
| AWS_REGION | The cluster’s region |
import os
import pandas as pd
base = os.environ["SEAGIT_BUCKET_URI"]
pd.DataFrame({"x": [1, 2, 3]}).to_csv(base + "examples/hello.csv", index=False)
print(pd.read_csv(base + "examples/hello.csv"))Access is shared: every developer on the hub can read and write the whole bucket or folder. Use separate stacks for teams that must not see each other’s data.
Network and security
- Allowed IPs limit who can reach the sign-in page at the load balancer. The default, 0.0.0.0/0, lets anyone reach it (sign-in is still required) and the Stack tab says so. Set your office or VPN addresses.
- Servers cannot reach the cloud metadata address, so a notebook cannot pick up the node’s own AWS permissions.
- Servers get no Kubernetes API token and cannot reach other buckets in the account.
- By default servers reach the internet only. Reach private addresses lets them connect to databases and services in the VPC and the cluster.
Use it from VS Code
- Open the hub, sign in and start your server.
- In the hub: File → Hub Control Panel → Token → Request new API token.
- In VS Code with the Jupyter extension: Select Kernel → Existing JupyterHub Server, then enter the hub address, your username and the token.
Changing and deleting
There is no Stop action for this stack: servers already stop when idle, and the hub itself is small.
Deleting the stack stops every server and deletes the hub, the load balancer and all home volumes. The delete dialog offers to also delete a bucket SeaGit created; left unticked, the bucket and its data stay in your account. A bucket you brought is never deleted.
Limits in this version
- No GPU sizes.
- No shared team folder between homes; share through the bucket.
- The hub is internet-facing with an IP allowlist; there is no private-only option yet.
- One hub per stack, on one cluster.
Troubleshooting
| Symptom | Cause and fix |
|---|---|
| A server never starts and times out after 10 minutes | No node has room for it. A server needs its guaranteed memory free on one node (see Sizes). Add a larger node pool or offer smaller sizes; the “X-Small, Small, Medium” choice exists for clusters with small nodes. |
| Deploy waits on “its TLS certificate” | cert-manager is still issuing the certificate (DNS validation takes a minute or two). If Let’s Encrypt fails an order, cert-manager 1.20 (the add-on version on Kubernetes 1.32 and later) retries after 5 minutes. |
| The sign-in page does not load | Your address is not in Allowed IPs, or a new load balancer is still warming up. SeaGit reports the stack ready only after the load balancer has been healthy for three minutes. |
| Sign-in with GitHub or SSO fails with a redirect error | Register the OAuth callback URL from the Stack tab with your provider. It is only known after the first deploy. |
| A notebook gets AccessDenied on S3 | Servers can use the stack’s bucket or folder only. Other buckets need their own access, which this template does not grant. |