Skip to content

Configuration

Dploy has two configuration surfaces:

  • The API is configured with environment variables (set via the Helm chart’s ConfigMap and Secret).
  • The operator reads a cluster-scoped OperatorConfig custom resource for cluster-wide defaults.
VariableDescription
JWKS_URLJWKS endpoint used to validate JWT signatures
JWT_ISSUERExpected JWT iss claim
VariableDefaultDescription
JWT_AUDIENCEdployExpected JWT aud claim
JWT_USERNAME_CLAIMnameClaim used as the username
OIDC_ISSUER$JWT_ISSUEROIDC issuer (internal, for token exchange)
OIDC_PUBLIC_ISSUER$OIDC_ISSUEROIDC issuer used for browser redirects
OIDC_CLIENT_IDdployOIDC client ID
OIDC_CLIENT_SECRETdploy-secretOIDC client secret
OIDC_REDIRECT_URLhttp://localhost:8080/auth/callbackOIDC callback URL
VariableDefaultDescription
DPLOY_NAMESPACEdploy-systemNamespace where DployTemplate/DployInstance CRs live
MAX_ENVIRONMENTS_PER_USER5Per-user quota (a template may override it)
DEFAULT_TTL86400Fallback initial TTL in seconds
EXTEND_TTL7200Fallback extension granted per /extend
VariableDefaultDescription
SERVER_HOST0.0.0.0Bind address
SERVER_PORT8080Port
DEBUGfalseVerbose logging

The operator reads a single cluster-scoped OperatorConfig named default. Any other name is ignored. It carries cluster-wide defaults that individual DployTemplates can override per-template.

apiVersion: dploy.dev/v1alpha1
kind: OperatorConfig
metadata:
name: default
spec:
# GitOps engine used when a template does not override it.
defaultEngine: flux # only "flux" is implemented today
# Defaults for the Flux engine.
flux:
namespace: flux-system # where HelmRelease + Sources are reconciled
serviceAccountName: "" # optional: SA the helm-controller impersonates
interval: 5m # GitRepository / HelmChart poll interval
# Used to build the default per-instance hostname: <name>-<uid>.<baseDomain>.
baseDomain: env.dploy.dev
# Cluster-wide default Go (text/template + sprig) for an instance's public URL.
# Overridable per template via DployTemplate.spec.connectionURLTemplate.
# Empty falls back to "https://<Host>".
connectionURLTemplate: "http://{{ .Host }}"
# Cluster-wide default presentation: "web" (clickable URL, redirect on ready)
# or "instructions" (copyable command, no redirect — see connectionMessageTemplate).
defaultConnectionType: web
# Cluster-wide default instructions template. Rendered with the same context as
# connectionURLTemplate plus .URL / .ConnectionURL (the resolved target).
# Only used when connectionType is "instructions".
connectionMessageTemplate: "" # e.g. "ssh root@{{ .ConnectionURL }} -p 22000"
# Applied to instances when their template omits the value.
defaults:
ttlSeconds: 86400 # initial TTL (24 h)
extendSeconds: 7200 # TTL bonus per /extend call (2 h)
maxExtends: 0 # 0 = unlimited extensions
maxInstancesPerUser: 5 # per-owner quota
# Free-form map exposed to value templates as `.Config.Values`. Anything you
# want common across templates (image registries, organisation toggles, …).
values:
registry: ghcr.io/your-org
ingressClass: nginx
FieldDescriptionDefault
defaultEngineGitOps engine: flux only today (argocd reserved)flux
flux.namespaceNamespace where the helm-controller reconciles HelmReleasesflux-system
flux.serviceAccountNameSA the helm-controller impersonates (optional)
flux.intervalPoll interval for GitRepository / HelmChart sources5m
baseDomainBase domain for the fallback <name>-<uid>.<baseDomain> host
connectionURLTemplateCluster default URL template (overridable per template)https://<Host>
defaultConnectionTypeweb (clickable) or instructions (copyable command)web
connectionMessageTemplateCluster default instructions template (when instructions)
defaults.ttlSecondsDefault TTL for a new instance86400
defaults.extendSecondsSeconds added per /extend call7200
defaults.maxExtendsMaximum extensions allowed (0 = unlimited)0
defaults.maxInstancesPerUserPer-owner quota (sanitized owner key)5
valuesFree-form map exposed as .Config.Values{}

connectionURLTemplate, connectionMessageTemplate (in OperatorConfig or per template) and valuesTemplate (per DployTemplate) are all rendered with Go text/template + sprig against this data:

VariableTypeNotes
.OwnerstringSanitized owner key — empty for unclaimed pool members and forbidden in pool valuesTemplate (CEL-enforced anonymity)
.UUIDstring8 hex chars, generated once by the operator, immutable in status.uuid
.BaseDomainstringCluster baseDomain
.HoststringPrecomputed <name>-<uid>.<baseDomain> — routing-neutral hostname
.URL / .ConnectionURLstringSet after connectionURLTemplate renders; only available in valuesTemplate and connectionMessageTemplate
.NamespacestringThe instance’s workload namespace
.Template*DployTemplateFull template spec, useful for {{ .Template.Name }} / {{ .Template.Spec.X }}
.Paramsmap[string]stringRequest-supplied params (nil in pool — anonymity)
.Claimsmap[string]anyRequester’s JWT claims (empty in pool — anonymity)
.Config.Valuesmap[string]anyOperatorConfig.spec.values

Overriding connectionURLTemplate per template

Section titled “Overriding connectionURLTemplate per template”

A DployTemplate.spec.connectionURLTemplate wins over the cluster default. Examples:

# Owner-based URL (leaks identity but useful for "your own session" UIs).
connectionURLTemplate: "https://{{ .Owner }}-{{ .UUID }}.{{ .BaseDomain }}"
# Path-based instead of subdomain (single ingress hostname, multiple instances).
connectionURLTemplate: "https://shared.example.com/{{ .UUID }}/"
# Custom port + scheme.
connectionURLTemplate: "https://{{ .Host }}:8443"

When a template (or the operator default) sets connectionType: instructions, the API doesn’t redirect on ready — it surfaces status.connectionMessage so the UI can show a copyable command:

# OperatorConfig (or DployTemplate.spec):
defaultConnectionType: instructions
connectionMessageTemplate: "ssh root@{{ .ConnectionURL }} -p 22000"
# … combined with a connectionURLTemplate that returns the bare host:
connectionURLTemplate: "{{ .Host }}"

Result: status.connectionMessage = "ssh root@vscode-abc123.example.com -p 22000", displayed by the UI without redirecting.

Usernames extracted from the JWT are sanitized for Kubernetes compatibility:

  • lowercased
  • . and @ replaced with -
  • any remaining non-[a-z0-9-] characters removed
  • leading/trailing - trimmed
John.Doe@example.com → john-doe-example-com
ResourcePatternExample (on-demand)Example (pool)
Workload namespace<owner>-<name>-<uid>john-doe-vscode-a1b2c3d4pool-webshell-c7218ff8
Default Host<name>-<uid>.<baseDomain>vscode-a1b2c3d4.env.dploy.devwebshell-c7218ff8.env.dploy.dev
DployInstance (on-demand)<owner>-<template>john-doe-vscode— (pool members get random suffixes: webshell-pool-XXXXX)

The UUID is 8 hex characters, generated once by the operator at the first reconcile and stored immutably in status.uuid. The workload namespace uses pool as its owner segment for unclaimed pool members; the Host template segment always reflects the DployTemplate name regardless of mode, so webshell-<uid> and kasm-<uid> are visibly distinct even before they’re claimed.