Deployment guide

JupyterHub

Overview

JupyterHub is a multi-user server that gives each person their own notebook environment. This SecureAnyCloud image runs JupyterHub from a dedicated Python virtual environment at /opt/jupyterhub, with JupyterLab and the classic notebook installed alongside, so every spawned single-user server has a modern interface.

What you get:

  • JupyterHub in a dedicated Python virtual environment, with JupyterLab and the classic notebook
  • The configurable HTTP proxy, provided by the official NodeSource Node.js LTS package
  • Authentication through the native authenticator extension, which provides an in-image signup and approval flow with no dependency on Linux system accounts
  • Single-user servers spawned by the SimpleLocalProcessSpawner, with no per-user system accounts required
  • A first administrator account (username SecureAnyCloud) and a fresh administrator password generated on the first boot of every deployed instance, so two instances launched from the same Amazon Machine Image never share passwords
  • The initial administrator password written to /root/jupyterhub-credentials.txt with mode 0600, readable only by the root user
  • JupyterHub listening on port 8000 on all interfaces, with no nginx reverse proxy in the image and no value to edit on first boot
  • 24/7 SecureAnyCloud support

Prerequisites

Before you deploy this image, make sure you have:

  • An Amazon Web Services account where you can launch EC2 instances
  • IAM permissions to launch instances, create security groups, and subscribe to AWS Marketplace products
  • An EC2 key pair in the target Region for SSH access to the instance
  • A VPC and subnet in the target Region, with a security group allowing inbound port 22 from your management network and inbound port 8000 from the networks your users will reach JupyterHub from
  • The AWS CLI (version 2) installed locally if you plan to deploy from the command line
1

Launch the instance from the AWS Marketplace

Sign in to the AWS Management Console, open the EC2 service, and select Launch instance. Under Application and OS Images, choose AWS Marketplace AMIs and search for JupyterHub. Select the SecureAnyCloud listing, choose Select, then choose Continue on the subscription summary.

Pick an instance type of m5.large or larger. JupyterLab kernels and the spawned notebook servers run inside the same instance, so size the VM for the number of concurrent users you expect. Leave the root volume at the default size or larger.

Choose your EC2 key pair under Key pair (login). Under Network settings, select your VPC and subnet, then create or select a security group that allows inbound port 22 from your management network and inbound port 8000 from the networks your users will reach JupyterHub from.

Select Launch instance. First-boot initialisation takes approximately one minute after the instance state becomes Running and the status checks pass.

2

Launch the instance from the AWS CLI

The following command launches an instance from the SecureAnyCloud JupyterHub Marketplace AMI into an existing subnet and security group.

Replace <ami-id> with the AMI ID shown on the Marketplace listing and <key-name> with your EC2 key pair name. Replace <subnet-id> with your subnet ID and <security-group-id> with a security group that opens ports 22 and 8000 as described above.

aws ec2 run-instances \
  --image-id <ami-id> \
  --instance-type m5.large \
  --key-name <key-name> \
  --subnet-id <subnet-id> \
  --security-group-ids <security-group-id> \
  --block-device-mappings '[{"DeviceName":"/dev/sda1","Ebs":{"VolumeSize":30,"VolumeType":"gp3"}}]' \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=jupyterhub-01}]'

On success, the command prints a JSON document. Note the instance ID, then retrieve the public address once the instance is running with aws ec2 describe-instances --instance-ids <instance-id> --query "Reservations[].Instances[].PublicIpAddress" --output text.

3

Connect and retrieve the initial credentials

Connect over SSH with the key pair you selected and the public IP address from Step 2. The SSH login user depends on the operating system of the AMI variant you launched:

AMI variant SSH login user

JupyterHub 5 on Ubuntu 24.04 ubuntu

The first-boot service runs before the SSH daemon becomes ready, so the credentials file is always in place when you sign in for the first time. Read it with sudo:

sudo cat /root/jupyterhub-credentials.txt

The plain-text file contains the JupyterHub URL, the administrator username (SecureAnyCloud) and the administrator password. Copy these values to a secure location such as a password manager or an encrypted vault. Do not commit them to source control.

From the same SSH session, confirm that the deployment is healthy:

curl -fsS -o /dev/null -w 'hub login HTTP %{http_code}\n' http://127.0.0.1:8000/hub/login
curl -fsS -o /dev/null -w 'hub health HTTP %{http_code}\n' http://127.0.0.1:8000/hub/health

A hub login HTTP 200 response and a hub health HTTP 200 response together confirm that JupyterHub, the configurable HTTP proxy, and the SQLite database are all serving.

4

Sign in to the JupyterHub web interface

Open a web browser and go to http://<public-ip>:8000/. JupyterHub shows the sign-in form.

Enter the administrator username SecureAnyCloud and the administrator password from /root/jupyterhub-credentials.txt, then select Sign In.

A banner about an unsecured HTTP connection is expected on a fresh deployment. Step 11 explains how to enable HTTPS in front of JupyterHub.

5

Explore the user home page

After you sign in, JupyterHub shows the user home page. From here you can start and stop your single-user server and switch to JupyterLab or the classic notebook. As an administrator, you can also reach the Token and Admin pages.

Select My Server to start a single-user JupyterLab session. The first start takes a few seconds while the spawner initialises the user's home directory under /srv/jupyterhub/users/<username> and launches the JupyterLab process.

6

Work in JupyterLab

When your server is running, you land in JupyterLab. The launcher offers a Python 3 notebook, a console, a terminal, and text, markdown and Python file editors. The left sidebar shows the file browser, running kernels, the Git extension and any installed JupyterLab extensions.

To switch to the classic notebook interface, change the URL fragment from /lab to /tree (for example http://<public-ip>:8000/user/sac/tree). The notebook directory for every user is /srv/jupyterhub/users/<username> on the instance, which is on the dedicated EBS data volume.

7

Add more users

The native authenticator gives JupyterHub an in-image signup flow. To add a second user, sign out and select Sign up on the login page, or visit http://<public-ip>:8000/hub/signup. Enter a username, an email address and a password.

The image is configured with NativeAuthenticator.open_signup = False, so a signup is not active until an administrator approves it. Sign back in as SecureAnyCloud, open the Admin page in the navigation bar, and approve the pending user.

From the Admin page you can also add users in bulk with Add Users, start or stop any user's server, and shut down the Hub. Use Access Server to reach each user's notebook environment.

8

Change the administrator password

Rotate the administrator password that was generated on first boot. Sign in as SecureAnyCloud, open the user dropdown next to the username in the top right, and choose Account Details. You can also go to http://<public-ip>:8000/hub/change-password. Enter the new password twice and select Update password.

If you lose the administrator password, reset it from an SSH session. The JupyterHub CLI idle-culler configuration helper is one option, but the simpler approach is to set a new password directly in the native authenticator database. The Python interpreter is at /opt/jupyterhub/bin/python and the database is at /srv/jupyterhub/jupyterhub.sqlite.

9

Install extra Python packages

The single-user servers spawned by JupyterHub run in the same Python virtual environment as JupyterHub itself, at /opt/jupyterhub. To add packages such as pandas, numpy or scikit-learn, install them into that virtual environment.

sudo /opt/jupyterhub/bin/pip install --no-cache-dir pandas numpy

When the install completes, restart any running single-user servers from the Admin page so that the running kernels pick up the new packages. Alternatively, restart JupyterHub:

sudo systemctl restart jupyterhub.service

This stops every single-user server and the Hub itself, then restarts the Hub. Users sign in again and start a fresh server.

10

Configure the spawner and authenticator

The JupyterHub configuration file is at /etc/jupyterhub/jupyterhub_config.py. The key settings you may want to change are:

  • c.Spawner.cpu_limit and c.Spawner.mem_limit to cap each user's resources
  • c.NativeAuthenticator.open_signup to open or close signups
  • c.NativeAuthenticator.minimum_password_length for the password policy
  • c.Authenticator.admin_users to add more administrators (first boot pre-approves the SecureAnyCloud admin)

After you edit the configuration, restart JupyterHub so that the Hub reads the new settings:

sudo systemctl restart jupyterhub.service

For deeper customisation, see the JupyterHub documentation at https://jupyterhub.readthedocs.io/.

11

Enable HTTPS in front of JupyterHub

Serve the Hub over HTTPS so that session cookies and authentication tokens cannot be intercepted. The simplest approach is to put nginx in front of JupyterHub on the same instance and let certbot manage the certificate.

The following steps assume that a DNS record points your fully qualified domain name at the instance's public IP address.

sudo apt-get update && sudo apt-get install -y nginx certbot python3-certbot-nginx
sudo tee /etc/nginx/sites-available/jupyterhub > /dev/null <<'NGINX' server { listen 80; server_name jupyterhub.your-domain.example; location / { proxy_pass http://127.0.0.1:8000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } NGINX
sudo ln -sf /etc/nginx/sites-available/jupyterhub /etc/nginx/sites-enabled/jupyterhub
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d jupyterhub.your-domain.example \
  --non-interactive --agree-tos -m you@your-domain.example \
  --redirect

After certbot finishes, restrict the security group so that port 8000 is no longer exposed to the internet and only ports 80 and 443 remain open.

12

Back up and maintain the instance

Back up two data sources together. The first is the JupyterHub SQLite database at /srv/jupyterhub/jupyterhub.sqlite, which holds users, tokens and server records. The second is the user notebook directories under /srv/jupyterhub/users/, which hold every user's notebooks and data.

sudo cp /srv/jupyterhub/jupyterhub.sqlite /var/backups/jupyterhub-db-$(date +%F).sqlite
sudo tar --acls --xattrs -czf /var/backups/jupyterhub-users-$(date +%F).tgz -C /srv/jupyterhub users

Copy both backups to an Amazon S3 bucket or another object store on a regular schedule. The /srv/jupyterhub mount is a dedicated EBS volume, so you can also use EBS snapshots for point-in-time recovery.

Ubuntu's unattended-upgrades is enabled by default for kernel and package updates. To update JupyterHub itself, upgrade the Python virtual environment:

sudo /opt/jupyterhub/bin/pip install --upgrade 'jupyterhub>=5,<6' jupyterlab notebook jupyterhub-nativeauthenticator
sudo systemctl restart jupyterhub.service
13

Scale beyond a single instance

For larger deployments, move away from the single-instance pattern:

  • Move single-user servers to Kubernetes with the Zero to JupyterHub with Kubernetes distribution and Amazon EKS, so each user's notebook runs in its own pod
  • Move the JupyterHub database from SQLite to Amazon RDS for PostgreSQL by setting c.JupyterHub.db_url
  • Replace SimpleLocalProcessSpawner with DockerSpawner or KubeSpawner for isolation between user notebooks
  • Replace the native authenticator with OAuthenticator (GitHub, Google, AWS Cognito, Okta) for single sign-on

The official JupyterHub documentation covers each of these options at https://jupyterhub.readthedocs.io/.

Screenshots

The JupyterHub sign-in page, served on first boot with no manual setup.

The JupyterHub user home page where the single-user server is started and stopped.

A JupyterLab session spawned by JupyterHub, with notebook, terminal and file browser.

The JupyterHub admin panel for approving signups and managing user servers.

Support

SecureAnyCloud provides 24/7/365 expert technical support for this image. Guaranteed response within 24 hours, one hour average for critical issues. Contact sales@secureanycloud.com.

For general JupyterHub administration questions, see the documentation at https://jupyterhub.readthedocs.io/ and the project's community at https://discourse.jupyter.org/.

Deploy on AWS

Launch JupyterHub on AWS with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on AWS