Skip to main content

Jupyter Deploy AWS EKS OIDC template

The AWS EKS OIDC Template deploys JupyterLab workspaces on an Amazon EKS cluster, with HTTPS, GitHub OAuth via Dex, and the Jupyter K8s operator for workspace lifecycle management.

Multiple users get isolated workspaces with persistent storage, idle shutdown, and role-based access control — all managed through jupyter-deploy.

Documentation: jupyter-deploy.readthedocs.io

The AWS EKS OIDC Template is maintained and supported by AWS.

10k View

When a user navigates to the deployment URL, they authenticate via GitHub through a Dex OIDC flow. Once authenticated, the Jupyter K8s operator provisions a workspace pod on the cluster with a dedicated persistent volume. Traefik routes HTTPS traffic to the workspace, and an authmiddleware component validates JWT session cookies on every request.

Administrators use jupyter-deploy to monitor cluster health, manage workspaces, and inspect platform components — without needing direct Kubernetes access.

Overview

Prerequisites

AWS account

The template creates AWS resources (VPC, EKS cluster, node groups, IAM roles, ECR, Route 53 records). 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 workspace URLs under a subdomain of 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 with GitHub identities. You grant access to GitHub teams, which must belong to your GitHub organization.

If you do not have a GitHub organization, you can create one using this guide.

Note: A personal app also works, but the organization may require third-party application approval before it can read private membership.

Next, create a new OAuth app:

  • for your organization: Settings → Developer settings → OAuth Apps → New OAuth App
  • for a personal app, log on to GitHub in your web browser, then use this link.

See the GitHub documentation for details.

Next, configure your OAuth app:

Field Value
Application name any, e.g. jupyter-deploy-eks-oidc
Homepage URL https://<subdomain>.<domain>
Authorization callback URL https://<subdomain>.<domain>/dex/callback

Finally, save the client ID, generate a client secret, and save that too. You will need them to run jd config.

Note: The callback URL uses /dex/callback (not /oauth2/callback as in the base template), because authentication goes through Dex as the OIDC identity provider. It must match your <subdomain> and <domain> exactly.

Usage

Installation

Recommended: create or activate a Python virtual environment.

uv add "jupyter-deploy[aws,k8s]" jupyter-deploy-tf-aws-eks-oidc

Or with pip:

pip install "jupyter-deploy[aws,k8s]" jupyter-deploy-tf-aws-eks-oidc

jupyter-deploy also needs these tools installed locally; jd config prompts you to install any that are missing: Terraform (>= 1.5.7), the AWS CLI, kubectl, Helm and yq.

Project setup

mkdir my-eks-deployment
cd my-eks-deployment

jd init . -E terraform -P aws -I eks -T oidc

Configure and deploy

jd config
jd up

The first deployment takes approximately 25-30 minutes.

User interface

# open the web UI
jd open

# configure kubectl to access your cluster
jd cluster login

Manage user workspaces

# list all workspaces
jd server list

# check workspace status
jd server status --name my-workspace --scope default

# open a running workspace in your web browser
jd open --server-name my-workspace --scope default

# stop/start a workspace
jd server stop --name my-workspace --scope default
jd server start --name my-workspace --scope default

# view workspace logs
jd server logs --name my-workspace --scope default

# open an interactive shell
jd server connect --name my-workspace --scope default

Manage cluster

# aggregated health check
jd health

# cluster control plane status
jd cluster status

# configure local kubeconfig
jd cluster login

Manage components

# health dashboard for all platform components
jd health --components

# list the platform components
jd component list

# view component logs
jd component logs --name dex

# restart a component (deployment only)
jd component restart --name oauth2-proxy

# reconcile a component (HelmRelease only; re-asserts desired state)
jd component reconcile --name workspace-router-chart

Take down

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

jd down

Architecture

Cluster and networking

The template creates a VPC with public and private subnets across two availability zones. EKS nodes run in private subnets; the Network Load Balancer (NLB) sits in public subnets and forwards HTTPS/443 to Traefik pods. Amazon Route 53 manages DNS via external-dns. Public TLS terminates at the NLB with an ACM certificate and is re-encrypted to Traefik, which presents a certificate from a private CA that cert-manager mints in the cluster.

Platform components

The platform layer handles routing, authentication, and workspace lifecycle:

  • Traefik — Ingress controller and reverse proxy
  • Dex — OIDC identity provider (federates to GitHub)
  • OAuth2 Proxy — Handles sign-in flow for unauthenticated users
  • Authmiddleware — Validates JWT session cookies via ForwardAuth
  • Jupyter K8s operator — Reconciles Workspace CRDs into Deployments and persistent volumes

Platform components

Authentication flow

Authentication uses a two-path model:

Browser access — OAuth2 Proxy redirects unauthenticated requests through Dex to GitHub. After authentication, Authmiddleware issues a JWT session cookie. Authmiddleware validates subsequent requests by checking the JWT directly.

CLI access — jd cluster login configures kubeconfig with an EKS exec-based auth provider using AWS IAM credentials, bypassing the OIDC flow entirely.

Authentication flow

Compute

Three kinds of node pool, each dedicated to a different job and each with its own autoscaling policy:

  • platform — EKS managed node group for the control-plane pods (operator, Karpenter, KEDA, cert-manager, external-dns); the Cluster Autoscaler sizes it
  • routing — Karpenter pool for the ingress and auth tier (Traefik, Dex, OAuth2 Proxy, Authmiddleware, web UI); always on, and grows with the KEDA-scaled routing pods
  • workspace pools — Karpenter pools for user workspace pods, one per workspace_nodepools entry plus an optional GPU pool; these scale to zero

A single jupyter-deploy/role Kubernetes label drives pod placement: every node carries it, and every pod selects its tier through a matching nodeSelector. The Karpenter pools carry the role as a taint as well, so a pod without the matching toleration can never land on them; the platform node group stays untainted. The platform node group auto-detects its AMI type from the instance family (CPU, GPU, Neuron; x86_64 or arm64).

Storage

Each workspace gets a dedicated EBS persistent volume mounted at /home/jovyan. Data persists across workspace stop/start cycles and pod restarts.

Custom workspace images

The template optionally creates ECR repositories and a CodeBuild project for building custom workspace images from Dockerfiles in the applications/ directory.

Details

Networking

The template creates a VPC with public and private subnets across two availability zones. EKS nodes run in private subnets; the Network Load Balancer (NLB) sits in public subnets and forwards HTTPS/443 to Traefik pods.

Amazon Route 53 manages DNS. The template references a Hosted Zone for your domain (which must already exist) and relies on external-dns to create DNS records pointing your subdomain to the NLB.

TLS

Public TLS terminates at the NLB with an AWS Certificate Manager (ACM) certificate that the template requests for <subdomain>.<domain> and validates via DNS against your hosted zone.

The NLB then re-encrypts to Traefik, so nothing crosses the VPC in plaintext. Traefik presents a certificate from a private CA that cert-manager mints inside the cluster; the NLB does not verify it, so it needs no public trust. cert-manager therefore makes no AWS API calls and holds no IAM role.

The NLB registers only nodes labeled jupyter-deploy/role=routing as targets, matching where Traefik runs.

Compute

The template creates a single platform EKS managed node group for control-plane-only pods, and Karpenter NodePools for the routing and workspace tiers. The managed node group auto-detects the appropriate EKS-optimized AMI type for its instance type; Karpenter nodes use the al2023@latest AMI alias.

Application Images

The template creates infrastructure for building custom workspace images:

  • ECR repository — One repository per application type. Stores the built container images.
  • CodeBuild project — Builds Dockerfiles from the applications/ directory and pushes to ECR.
  • IAM role — CodeBuild service role with permissions for ECR push and CloudWatch Logs.

IAM

The template creates several IAM roles:

  • Cluster role — The EKS control plane assumes this role
  • Node roles — One per node group, with managed policies for ECR pull, EKS worker nodes, and CNI
  • Pod identity associations — external-dns uses EKS Pod Identity for Route 53 access
  • Admin access entries — The template always authorizes the caller's IAM principal. Roles in admin_role_names and users in admin_user_names get cluster admin and workspace admin permissions. For stable state across caller switches, list every role/user that may run jd config or jd up
  • Workspace Bedrock roles — One per workspace namespace with Bedrock access, bound to the namespace's workspace service account through EKS Pod Identity, so Claude Code and Codex work in workspaces with no setup

Helm charts

Chart Namespace Purpose
traefik-crds Router namespace Traefik CRDs (IngressRoute, Middleware, etc.)
jupyter-k8s Operator namespace Workspace operator, extension API server, CRDs
jupyter-k8s-aws-oidc Router namespace Traefik, Dex, OAuth2 Proxy, Authmiddleware, web UI
karpenter karpenter Karpenter node-autoscaler controller
karpenter-nodepools (local) karpenter Karpenter NodePool + EC2NodeClass definitions (routing, workspaces)
keda keda Event-driven autoscaling for the routing tier
cluster-autoscaler kube-system Autoscaler for the platform managed node group
prometheus monitoring Metrics server (scaling source for KEDA)
aws-for-fluent-bit kube-system Pod log shipping to CloudWatch (optional, enable_component_logging)
nvidia-device-plugin kube-system Registers GPU capacity on GPU pool nodes (optional; a pool entry setting accelerator = "nvidia" pulls it in)
github-rbac (local) Shared namespace Namespace-scoped RBAC for the GitHub teams of each workspace namespace
workspace-defaults (local) Shared namespace WorkspaceTemplates and workspace-ingress NetworkPolicy in each workspace namespace

RBAC

The template deploys a github-rbac local chart that creates namespace-scoped Role and RoleBinding resources. Each workspace namespace gets a Role granting workspace CRUD permissions, which a RoleBinding ties to its GitHub teams: with workspace_namespaces empty, default is the only workspace namespace and binds every team in oauth_allowed_teams; otherwise each entry creates a namespace (or adopts default) bound to the entry's teams only. Every oauth_allowed_teams team also gets a read-only (get/list) Role in workspace_shared_namespace for discovering shared WorkspaceTemplate and WorkspaceAccessStrategy resources.

Presets

The template provides two variable presets:

  • defaults-all.tfvars — comprehensive preset with all recommended values
  • defaults-base.tfvars — minimal preset that additionally prompts for node group configuration

Inputs

Name Type Default Description
cluster_name_prefix string jupyter-deploy-eks Prefix for the EKS cluster name (template appends a unique suffix)
region string us-west-2 AWS region where to deploy the resources
kubernetes_version string 1.36 Kubernetes version for the EKS cluster
domain string Required Domain name for workspace URLs (must have a Route 53 hosted zone)
subdomain string Required Subdomain prefix for workspace URLs
oauth_app_client_id string Required Client ID of the GitHub OAuth app
oauth_app_client_secret string Required Client secret of the GitHub OAuth app
oauth_allowed_teams list(string) Required GitHub teams to allow access, in org:team format
platform_instance_types list(string) ["m5.large"] Instance types for the platform managed node group
platform_disk_size_gb number 50 Root volume size for platform nodes
platform_min_size number 2 Minimum size of the platform node group (one node per AZ)
platform_max_size number 3 Maximum size of the platform node group
routing_instance_categories list(string) ["c", "m"] Instance categories Karpenter may pick for routing nodes
routing_instance_generation_min string 6 Minimum instance generation for routing nodes
routing_disk_size_gb number 50 Root volume size for routing nodes
routing_max_cpu string 32 Ceiling on total vCPU of the routing pool
routing_max_memory string 128Gi Ceiling on total memory of the routing pool
workspace_nodepools list(map(string)) one workspace-cpu pool Karpenter workspace pools, each with its own instance families and CPU/memory ceilings
node_expire_after string 504h Maximum node lifetime before Karpenter recycles it
workspace_namespaces list(map(string)) [] Workspace namespaces and the GitHub teams granted each; empty = every team in default
admin_role_names list(string) [] IAM role names to grant cluster and workspace admin (list all callers for stable state)
admin_user_names list(string) [] IAM user names to grant cluster and workspace admin (list all callers for stable state)
cluster_log_retention_days number 30 Days to retain EKS cluster CloudWatch logs
custom_tags map(string) {} Tags added to all AWS resources
workspace_operator_namespace string jupyter-k8s-system Namespace for the workspace operator
workspace_router_namespace string jupyter-k8s-router Namespace for routing components
workspace_shared_namespace string jupyter-k8s-shared Namespace for shared workspace resources
workspace_operator_chart_oci string See preset OCI reference for the jupyter-k8s Helm chart
workspace_operator_chart_version string See preset Version of the jupyter-k8s chart
workspace_router_chart_oci string See preset OCI reference for the aws-oidc router chart
workspace_router_chart_version string See preset Version of the aws-oidc chart
traefik_crd_chart_version string 1.15.0 Version of the Traefik CRDs chart
workspaces_default_access_type string OwnerOnly Default access type for new workspaces (OwnerOnly or Public)
workspaces_default_ownership_type string OwnerOnly Default ownership type for new workspaces
workspaces_idle_shutdown_enabled bool true Enable automatic idle shutdown for workspaces
workspaces_idle_shutdown_timeout_default number 60 Default idle timeout in minutes (5-1440)
workspaces_idle_shutdown_timeout_min number 15 Minimum idle timeout users can set (advanced; lower values mainly serve testing)
workspaces_idle_shutdown_timeout_max number 480 Maximum idle timeout users can set (15-1440)
workspace_app_jupyterlab_use bool true Build and deploy the JupyterLab workspace image
workspace_app_jupyterlab_app_type string jupyterlab Application type identifier for the workspace template
workspace_app_jupyterlab_image_name string See preset ECR repository name for the JupyterLab image
workspace_app_jupyterlab_image_build string v1 Build tag (increment to trigger rebuild)
workspace_app_claude_code_version string See preset Claude Code version installed in the JupyterLab image
workspace_app_codex_version string See preset Codex CLI version installed in the JupyterLab image
workspace_bedrock_enabled bool true Give workspaces Bedrock credentials for Claude Code and Codex; a workspace_namespaces entry opts out with bedrock = "false"
workspace_bedrock_inference_scope string geo Inference profiles workspaces use: geo (requests stay in the region's geography) or global
workspace_bedrock_model_patterns list(string) ["anthropic.claude-*", "openai.gpt-*"] Bedrock model IDs workspaces may invoke (IAM wildcards)
workspace_templates list(map(string)) [] Named workspace template configs that pool entries offer as cards via their templates key
enable_default_gpu_pool bool false Append the built-in workspace-gpu entry and its jupyterlab-gpu template config; combining it with your own accelerator entries raises a plan-time error
nvidia_device_plugin_version string 0.20.1 Version of the NVIDIA device plugin chart (the template installs it when any pool entry sets accelerator = "nvidia")

Outputs

Name Description
cluster_name Name of the EKS cluster
cluster_endpoint API server endpoint URL for the EKS cluster
platform_mng_names Names of the EKS managed node groups
cluster_arn ARN of the EKS cluster
cluster_ca_certificate Base64-encoded CA certificate for the EKS cluster
region AWS region hosting the cluster
deployment_id Unique deployment identifier
vpc_id ID of the VPC hosting the EKS cluster
workspace_operator_namespace Kubernetes namespace for the workspace operator controller
workspace_router_namespace Kubernetes namespace for routing components
workspace_shared_namespace Kubernetes namespace for shared workspace resources
workspace_base_url Base URL for workspace access
get_started_url URL to the web UI
secret_arn ARN of the Secrets Manager secret storing the OAuth app client secret
jupyterlab_image_uri ECR image URI for the JupyterLab workspace image
kubeconfig_path Path to the local kubeconfig file for this cluster
workspace_bedrock_roles JSON-encoded map of workspace namespace to the IAM role granting its workspaces Bedrock
acm_certificate_arn ARN of the ACM certificate for the deployment domain

License

The AWS EKS OIDC Template is licensed under the MIT License.

Metadata

Release files for jupyter-deploy-tf-aws-eks-oidc 0.4.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for jupyter-deploy-tf-aws-eks-oidc 0.4.1
File Size Uploaded
jupyter_deploy_tf_aws_eks_oidc-0.4.1.tar.gz 180.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for jupyter-deploy-tf-aws-eks-oidc 0.4.1
File Interpreter ABI Platform
jupyter_deploy_tf_aws_eks_oidc-0.4.1-py3-none-any.whl Python 3 none any Details

Total release size: 314.6 kB

Release files / jupyter_deploy_tf_aws_eks_oidc-0.4.1.tar.gz

Download URL jupyter_deploy_tf_aws_eks_oidc-0.4.1.tar.gz
Size 180.7 kB
Tags Source
SHA-256 checksum
How to use checksums
b3ee2ed6a217aed4f25bcb5d1e8640a5e38a3ebc09edbe884e43d610af67912a
BLAKE2b-256 checksum
How to use checksums
26f07fdb2c7cae3c96538198869c5da82774e0b5b0069c69f1abbbf8e742ff75
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","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}

Release files / jupyter_deploy_tf_aws_eks_oidc-0.4.1-py3-none-any.whl

Download URL jupyter_deploy_tf_aws_eks_oidc-0.4.1-py3-none-any.whl
Size 133.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
692d1eb5a7a876e2032a7fe5af81c112adbb990713eb279c21a3a4c4dbd78bf8
BLAKE2b-256 checksum
How to use checksums
367e8c7887c43f399080b3f2df4aaa301b87f272f3423f2abb3379ab85f672a6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","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}

Release history Release notifications | RSS feed

This release

0.4.1 This release

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page