Skip to content

Authoring Workshop Templates

A workshop template is a single YAML file that describes one entry in Orchestra’s catalog — the RStudio, JupyterLab, or other environment a participant can launch. It’s a friendly projection of the Workshop CRD spec: a handful of fields (image, port, resources, duration…) that the platform turns into a fully-formed Workshop custom resource on each launch.

Templates are git-managed (ADR-0006). They live in the Helm chart at:

deploy/charts/orchestra/files/templates/
├── jupyter.yaml
├── rstudio.yaml
├── template.schema.json # generated — do not hand-edit
└── README.md

These files are the source of truth, not the database. The chart renders them into a ConfigMap mounted into the API pod, which loads them into an in-memory registry at startup — there is no admin UI and no runtime database toggle. A catalog change is a change to these files, whether the submission form writes it for you (the front door) or you hand-author the file and open the PR yourself (the fallback).

  • One file per template, named <slug>.yaml. The filename stem must equal the template’s slug (validation enforces this).
  • Slugs must be unique across the directory.
  • To retire a template without deleting its file, set enabled: false.

Every field below is defined by the WorkshopTemplateFile model in the orchestra-template-tools package — the single source of truth from which template.schema.json is generated. Fields accept camelCase (shown here) or snake_case; the schema uses camelCase.

FieldTypeRequiredDefaultNotes
namestringyesHuman-readable display name shown in the catalog.
slugstringyesk8s-safe id: lowercase alphanumerics and -, must start/end alphanumeric, max 40 chars. Prefixes instance names; must match the filename stem.
descriptionstring | nullnonullOptional catalog description.
imagestringnorocker/rstudio:latestDefault container image for the session.
defaultDurationstringno4hDefault session lifetime (e.g. 2h, 4h).
portinteger (1–65535)no8787Port the app listens on inside the container (RStudio 8787, JupyterLab 8888). The sidecar proxies to http://localhost:<port>.
envmap<string,string>no{}Extra environment variables for the app container. Override operator defaults such as DISABLE_AUTH.
argslist<string>no[]Container args, replacing the image’s default CMD (e.g. JupyterLab launch flags). Leave empty to use the image default.
tiersmall | largenosmallTenant node-pool tier. Maps to nodeSelector/tolerations in the operator when tenant pools are enabled.
resourcesobject (see below)nomodel defaultsCPU/memory/ephemeral-storage limits and requests.
storageobject | nullnonullPer-session persistent volume. Unset means no dedicated PVC beyond the defaults.
tagslist<string>no[]Category tags for catalog filtering.
enabledbooleannotrueWhether the template is shown and launchable. false retires it without deleting the file.
urlstring | nullnonullLanding/materials page for the catalog’s “Learn more” link (the form’s Landing URL). Catalog metadata; never reaches the CRD.
sourceUrlstring | nullnonullRepository the workshop content lives in, for the catalog’s “Source” link (the form’s Source repo URL). Catalog metadata; never reaches the CRD.
submittedBystring | nullnonullGitHub handle of the submitter, stamped by the front door from the issue author (ADR-0009); absent on hand-authored templates — you don’t set this.

All resource fields are strings using Kubernetes quantity notation (e.g. "2", 500m, 4Gi).

FieldTypeRequiredDefaultNotes
cpustringno1CPU limit.
memorystringno2GiMemory limit.
cpuRequeststringno500mCPU request.
memoryRequeststringno1GiMemory request.
ephemeralStoragestringno8GiEphemeral-storage limit — everything written outside the /data PVC (package installs, /tmp, the container writable layer). The kubelet evicts the pod if exceeded.
ephemeralStorageRequeststringno8GiEphemeral-storage request.
FieldTypeRequiredDefaultNotes
sizestringno10GiPersistent volume size for the session’s /data.
storageClassstring | nullnonullStorage class name. Leave unset (or empty) to use the cluster default.

Copy this to <slug>.yaml, rename the slug (and file) to match, and edit. It validates as-is:

# yaml-language-server: $schema=./template.schema.json
name: My Workshop
slug: my-workshop
description: >-
A one-line description of what participants get.
image: bioconductor/bioconductor_docker:RELEASE_3_20
defaultDuration: 4h
port: 8787
tier: small
resources:
cpu: '2'
memory: 4Gi
cpuRequest: '1'
memoryRequest: 2Gi
ephemeralStorage: 8Gi
ephemeralStorageRequest: 8Gi
storage:
size: 10Gi
tags:
- bioconductor
enabled: true

For JupyterLab and other stock images, see Configuring a Workshop Image for the exact port, env, and args needed to start the app unauthenticated behind the proxy.

The first line of every template is a schema directive:

# yaml-language-server: $schema=./template.schema.json

This is a repo-relative path to the generated template.schema.json that sits next to the templates — so validation works offline, in any checkout, with no URL to rot. With the YAML extension (VS Code, or any editor speaking the YAML language server), you get autocomplete, type checking, and required-field warnings as you type.

The schema is generated, never hand-edited. Regenerate it after changing the model:

Terminal window
just template-schema

Validate every file in the directory with the same CLI that CI runs:

Terminal window
just validate-templates

This checks each file against the schema, and catalog-level rules: unique slugs and filename-matches-slug. It exits non-zero on any error.

  1. Edit a YAML file. Copy an existing template (e.g. rstudio.yaml) to <slug>.yaml and change the fields. Your editor validates against the schema as you go.

  2. Validate locally with just validate-templates (and just check-schema if you touched the model).

  3. Open a PR. CI runs the same validation on every file.

  4. An admin reviews and merges. Templates are the platform’s catalog, so changes go through review.

  5. Deploy rolls it out. The next helm upgrade re-renders the ConfigMap and restarts the API pod, which reloads the registry from the files. The new or changed template appears in the catalog.