Skip to main content

Base terraform template to deploy a JupyterLab application on an AWS EC2 instance.

Project description

Jupyter Deploy AWS EC2 base template

The AWS Base Template deploys a JupyterLab application to a dedicated Amazon EC2 instance, served on your domain with encrypted HTTPS, GitHub OAuth integration, real-time collaboration, and fast UV-based environments.

Documentation: jupyter-deploy.readthedocs.io

The AWS Base Template is maintained and supported by AWS.

10k View

When you navigate to the application URL in your web browser, you connect to the EC2 instance over HTTPS. On the first visit, the instance redirects to GitHub for OAuth authentication; once verified, you connect to the jupyter container within your EC2 instance and see a JupyterLab application in your web browser.

Overview

Prerequisites

AWS account

The template needs to create AWS resources. Your local environment needs access to valid AWS credentials.

If you do not have an AWS account, follow the official guide to create one.

If you already have an AWS account, make sure your CLI credentials are configured.

Get and register a domain

The template serves your JupyterLab app to a URL in your own domain. You will need to specify this domain when you configure the project.

If you already own a domain, register it with Amazon Route 53 using this guide.

If you do not own a domain yet, you can buy one through Amazon Route 53 using this guide.

Setup a GitHub OAuth app

The template gates access to your JupyterLab application with GitHub identities. You will need to create a GitHub OAuth app for this purpose.

First, log on to GitHub on your web browser, then use this link to create a new OAuth app.

You can choose any name for the application; for example jupyter-deploy-aws-base.

Set Homepage URL to: https://<subdomain>.<domain>; for example https://jupyterlab-app.<domain>. <domain> corresponds to the domain above. You can choose any <subdomain> you like, but it must be a valid domain part (lowercase letters, digits, or hyphens).

Set Authorization callback URL to: https://<subdomain>.<domain>/oauth2/callback. Make sure it matches the <domain> and <subdomain> exactly.

In the next page, write down and save your app client ID. Generate an app client secret, and write it down as well.

Refer to GitHub documentation for more details.

Usage

This terraform project is meant to be used with the jupyter-deploy CLI.

Installation

Recommended: create or activate a Python virtual environment.

uv add jupyter-deploy[aws] jupyter-deploy-tf-aws-ec2-base

Or with pip:

pip install jupyter-deploy[aws] jupyter-deploy-tf-aws-ec2-base

Project setup

mkdir my-jupyter-deployment
cd my-jupyter-deployment

jd init . -E terraform -P aws -I ec2 -T base

Consider making my-jupyter-deployment a git repository.

Configure and create the infrastructure

jd config
jd up

Access your JupyterLab application

# verify that your host and containers are running
jd host status
jd server status

# open your application on your web browser
jd open

Manage access

# By GitHub users
jd users list
jd users add USERNAME1 USERNAME2
jd users remove USERNAME1

# By GitHub organization
jd organization get
jd organization set ORGANIZATION
jd organization unset

# Along with GitHub organization, by teams
jd teams list
jd teams add TEAM1 TEAM2
jd teams remove TEAM2

Temporarily stop/start your EC2 instance

# To stop your instance
jd host stop
jd host status

# To start it again
jd host start
jd server start
jd server status

Manage your EC2 instance

# connect to your host
jd host connect

# disconnect
exit

Take down all the infrastructure

This operation removes all the resources associated with this project in your AWS account.

jd down

Architecture

Containers

The application runs as a set of containerized services orchestrated by Docker Compose. Traefik acts as the reverse proxy — it handles TLS termination, routes incoming HTTPS requests, and delegates authentication decisions to OAuth2 Proxy via the ForwardAuth middleware. OAuth2 Proxy manages the full OAuth 2.0 flow with GitHub as the identity provider, handling redirects, token validation, and session cookies. Traefik forwards authenticated requests to the JupyterLab container. A Fluent Bit sidecar collects service logs and a logrotate container manages log retention on disk.

Containers

Authentication flow

On first visit, Traefik forwards the request to OAuth2 Proxy, which redirects the browser to GitHub for authentication. GitHub prompts the user to authorize the OAuth App, then redirects back to OAuth2 Proxy with an authorization code. OAuth2 Proxy exchanges the code for an access token, verifies the user's identity against the configured allowlist, sets a session cookie, and lets the request through to JupyterLab. The session cookie authenticates subsequent requests without repeating the OAuth dance. See the OAuth2 Proxy GitHub provider documentation for details.

Authentication flow

Details

Networking

The template places the EC2 instance in the first subnet of the default VPC in the selected AWS region. The template assigns an Elastic IP (EIP) to the instance to keep its public IP address stable across stop/start cycles.

Amazon Route 53 manages DNS. The template references a Hosted Zone for your domain (which must already exist) and adds a DNS record pointing your subdomain to the instance's Elastic IP.

The instance's security group only allows ingress on port 443 (HTTPS). There is no SSH access — all administrator operations go through AWS Systems Manager (SSM).

Compute

The template selects the latest Amazon Linux 2023 AMI compatible with the chosen instance type:

  • Standard AL2023 AMI for CPU instances (x86_64 or arm64)
  • Deep Learning AMI (DLAMI) for GPU or Neuron instances (x86_64 or arm64)

You can also provide a specific AMI ID to override automatic selection.

Storage

The instance has two volumes. The root volume inherits its size and settings from the selected AMI, with a configurable minimum size. It persists across instance restarts and instance type changes, as long as the new instance type is compatible with the existing root volume. The template attaches a separate EBS data volume and mounts it into the JupyterLab container at /home/jovyan — this volume persists user data across container restarts and instance stop/start cycles.

You can optionally attach additional EBS volumes or EFS file systems and mount them into the Jupyter home directory.

TLS

Let's Encrypt provides TLS certificates using the ACME protocol. Traefik acts as the ACME client and proves domain ownership via a DNS-01 challenge: it creates a temporary TXT record in the Route 53 Hosted Zone, Let's Encrypt verifies it, and issues the certificate. Traefik stores the certificate in acme.json and renews it automatically. The template also backs the certificate up to AWS Secrets Manager so it persists across instance replacements.

IAM and Secrets

The template creates an IAM role for the EC2 instance with permissions for SSM, Route 53, S3, and (optionally) EFS access.

The template creates two AWS Secrets Manager secrets:

  • One to store the OAuth App client secret
  • One to store TLS certificates from Let's Encrypt, ensuring they persist across instance replacements

Deployment Configuration

An S3 bucket stores all deployment configuration files: bash scripts, Docker service definitions, and application configuration. The instance pulls these files during setup or updates via a cloud-init script.

The template creates an SSM startup document that orchestrates instance configuration using these files:

File Purpose
cloudinit.sh.tftpl EC2 instance configuration
docker-compose.yml.tftpl Docker service definitions
docker-startup.sh.tftpl Docker service startup
cloudinit-volumes.sh.tftpl Optional EBS/EFS volume mounts
traefik.yml.tftpl Traefik reverse proxy configuration
dockerfile.jupyter Jupyter container image
jupyter-start.sh Jupyter container entrypoint
jupyter-reset.sh Fallback if Jupyter fails to start
pyproject.jupyter.toml Python dependencies for the Jupyter environment
jupyter_server_config.py Jupyter server settings
dockerfile.logrotator Log rotation sidecar container
logrotator-start.sh.tftpl Logrotate configuration
fluent-bit.conf Fluent-bit log collection configuration
parsers.conf Fluent-bit Docker log parsers

If you selected pixi as the dependency manager, the template uses pixi.jupyter.toml instead of pyproject.jupyter.toml.

An SSM association triggers the startup script on the instance whenever the configuration changes.

Operations

The template creates SSM documents that the jd CLI uses to manage the deployment remotely:

Document Purpose
check-status-internal.sh Verify services are running and TLS certificates are available
get-status.sh Translate status checks to human-readable output
update-auth.sh Update authorized org, teams, and/or users
get-auth.sh Retrieve current authorization settings
update-server.sh Update running services
refresh-oauth-cookie.sh Rotate the OAuth cookie secret and invalidate all sessions

Logging

Fluent-bit collects Docker service logs and writes them to /var/log/services on the instance volume. A logrotate sidecar container handles automatic rotation of all log files based on configurable size and retention settings.

Presets

The template provides two variable presets:

  • defaults-all.tfvars — comprehensive preset with all recommended values
  • defaults-base.tfvars — minimal preset that prompts for instance type and volume size

Requirements

Name Version
terraform >= 1.0
aws >= 4.66

Providers

Name Version
aws >= 4.66

Modules

Name Location
ami_al2023 template/engine/modules/ami_al2023
certs_secret template/engine/modules/certs_secret
ec2_iam_role template/engine/modules/ec2_iam_role
ec2_instance template/engine/modules/ec2_instance
network template/engine/modules/network
s3_bucket template/engine/modules/s3_bucket
secret template/engine/modules/secret
volumes template/engine/modules/volumes

Resources

Name Type
aws_security_group resource
aws_instance resource
aws_iam_role resource
aws_iam_role_policy_attachment resource
aws_iam_instance_profile resource
aws_ebs_volume resource
aws_volume_attachment resource
aws_ssm_document resource
aws_ssm_association resource
aws_route53_zone resource
aws_route53_record resource
aws_secretsmanager_secret resource
aws_iam_policy resource
aws_ssm_parameter resource
null_resource resource
aws_default_vpc resource
aws_ebs_volume resource
aws_efs_file_system resource
aws_efs_mount_target resource
aws_eip resource
aws_s3_bucket resource
aws_s3_bucket_versioning resource
aws_s3_bucket_server_side_encryption_configuration resource
aws_s3_bucket_public_access_block resource
aws_s3_object resource
aws_subnets data source
aws_subnet data source
aws_ami data source
aws_route53_zone data source
aws_ebs_volume data source
aws_efs_file_system data source
aws_iam_policy data source
aws_iam_policy_document data source
local_file data source

Inputs

Name Type Default Description
region string us-west-2 The AWS region where to create the resources
instance_type string t3.medium The type of instance to start
key_pair_name string null The name of key pair
ami_id string null The ID of the AMI to use for the instance
min_root_volume_size_gb number 30 The minimum size in gigabytes of the root EBS volume for the EC2 instance (will use AMI snapshot size if larger)
volume_size_gb number 30 The size in GB of the EBS volume the Jupyter Server has access to
volume_type string gp3 The type of EBS volume the Jupyter Server will has access to
iam_role_prefix string Jupyter-deploy-ec2-base The prefix for the name of the IAM role for the instance
oauth_app_secret_prefix string Jupyter-deploy-ec2-base The prefix for the name of the AWS secret to store your OAuth app client secret
s3_bucket_prefix string jupyter-deploy-ec2-base The prefix for the name of the S3 bucket where deployment scripts are stored (3-28 characters, lowercase alphanumeric with hyphens)
certs_secret_prefix string Jupyter-deploy-ec2-base The prefix for the name of the AWS secret where ACME certificates are stored
letsencrypt_email string Required An email for letsencrypt to notify about certificate expirations
domain string Required A domain that you own
subdomain string Required A sub-domain of domain to add DNS records
oauth_provider string github The OAuth provider to use
oauth_allowed_org string "" The GitHub organization to allowlist
oauth_allowed_teams list(string) [] The list of GitHub teams to allowlist
oauth_allowed_usernames list(string) [] The list of GitHub usernames to allowlist
oauth_app_client_id string Required The client ID of the OAuth app
oauth_app_client_secret string Required The client secret of the OAuth app
log_files_rotation_size_mb number 50 The size in megabytes at which to rotate log files
log_files_retention_count number 10 The maximum number of rotated log files to retain for a log group
log_files_retention_days number 180 The maximum number of days to retain any log files
custom_tags map(string) {} The custom tags to add to all the resources
additional_ebs_mounts list(map(string)) [] Elastic block stores to mount on the notebook home directory
additional_efs_mounts list(map(string)) [] Elastic file systems to mount on the notebook home directory

Outputs

Name Description
jupyter_url The URL to access your notebook app
auth_url The URL for the OAuth callback - do not use directly
instance_id The ID of the EC2 instance
ami_id The Amazon Machine Image ID used by the EC2 instance
jupyter_server_public_ip The public IP assigned to the EC2 instance
secret_arn The ARN of the AWS Secret storing the OAuth client secret
certs_secret_arn The ARN of the AWS Secret where TLS certificates are stored
deployment_scripts_bucket_name Name of the S3 bucket where deployment scripts and service configuration files are stored
deployment_scripts_bucket_arn ARN of the S3 bucket where deployment scripts and service configuration files are stored
region The AWS region where the resources were created
deployment_id Unique identifier for this deployment
images_build_hash Hash of files affecting docker compose image builds (jupyter, log-rotator)
scripts_files_hash Hash of all deployment script files which controls SSM association re-execution
server_status_check_document Name of the SSM document to verify if the server is ready to serve traffic
server_update_document Name of the SSM document to control server container operations
server_logs_document Name of the SSM document to print server logs to terminal
server_exec_document Name of the SSM document to execute commands inside server containers
server_connect_document Name of the SSM document to start interactive shell sessions inside server containers (jupyter or traefik)
auth_org_unset_document Name of the SSM document to remove the allowlisted organization
auth_check_document Name of the SSM document to view authorized users, teams and organization
auth_users_update_document Name of the SSM document to change the authorized users
auth_teams_update_document Name of the SSM document to change the authorized teams
auth_org_set_document Name of the SSM document to allowlist an organization
auth_org_unset_document Name of the SSM document to remove the allowlisted organization
persisting_resources List of identifiers of resources that should not be destroyed

License

The AWS Base Template is licensed under the MIT License.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jupyter_deploy_tf_aws_ec2_base-0.5.1.tar.gz (96.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jupyter_deploy_tf_aws_ec2_base-0.5.1-py3-none-any.whl (86.8 kB view details)

Uploaded Python 3

File details

Details for the file jupyter_deploy_tf_aws_ec2_base-0.5.1.tar.gz.

File metadata

  • Download URL: jupyter_deploy_tf_aws_ec2_base-0.5.1.tar.gz
  • Upload date:
  • Size: 96.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for jupyter_deploy_tf_aws_ec2_base-0.5.1.tar.gz
Algorithm Hash digest
SHA256 abdf3afc2f0a7e31228789ebb488e213c99c8055d61788ff17d16a4fc0fa9a4f
MD5 8038543a09025fcac9c24c20c9feeee7
BLAKE2b-256 2df49c485d55af5ddce219234e45612b3699477f70f6422c989e1d562bcaafb6

See more details on using hashes here.

File details

Details for the file jupyter_deploy_tf_aws_ec2_base-0.5.1-py3-none-any.whl.

File metadata

  • Download URL: jupyter_deploy_tf_aws_ec2_base-0.5.1-py3-none-any.whl
  • Upload date:
  • Size: 86.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for jupyter_deploy_tf_aws_ec2_base-0.5.1-py3-none-any.whl
Algorithm Hash digest
SHA256 8806144b4e3288b654194ddfcf537ebbcf40fb4f2001a733c6948b6dd6886471
MD5 db019fd7263a901cc42b531482f151e0
BLAKE2b-256 0062f05bf3a1f55eff6175f4e024315130ed46b9463f9c4ae9b44004dfc2b8ce

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page