Skip to main content
k8s-scheduler is configured through environment variables. Different variables apply to the server and operator components.

Server Configuration

The server (cmd/server) accepts configuration via environment variables or Vault-injected secrets.

Always Required

DATABASE_DSN
string
required
PostgreSQL connection string.
DATABASE_DSN="postgres://user:password@localhost:5432/scheduler?sslmode=disable"
For production, enable SSL:
DATABASE_DSN="postgres://user:[email protected]:5432/scheduler?sslmode=require"

Required in Production

These can be skipped when DEV_MODE=true for local development.
GOOGLE_CLIENT_ID
string
required
Google OAuth2 client ID from Google Cloud Console.
GOOGLE_CLIENT_ID="your-client-id.apps.googleusercontent.com"
GOOGLE_CLIENT_SECRET
string
required
Google OAuth2 client secret.
GOOGLE_CLIENT_SECRET="your-client-secret"
GOOGLE_REDIRECT_URL
string
required
OAuth callback URL. Must match the authorized redirect URI in Google Cloud Console.
GOOGLE_REDIRECT_URL="https://app.example.com/oauth2/callback"

Server Settings

DEV_MODE
boolean
default:"false"
Enable local development mode. Disables OAuth and auto-logs in as dev@localhost.
DEV_MODE="true"
Never enable in production. This bypasses all authentication.
REACT_UI
boolean
default:"false"
Serve embedded React SPA from go:embed.
REACT_UI="true"
Set to false if serving the frontend separately (e.g., via CDN or Vite dev server).
SERVER_ADDR
string
default:":8081"
HTTP server listen address.
SERVER_ADDR=":8081"
BASE_URL
string
default:"http://localhost:8081"
Public URL of the application. Used for OAuth redirects and email links.
BASE_URL="https://app.example.com"
DEPLOYMENT_DOMAIN
string
default:"example.com"
Base domain for user deployment ingresses.
DEPLOYMENT_DOMAIN="example.com"
User deployments get subdomains like:
https://user-123-my-app.example.com
KUBERNETES_ENABLED
boolean
default:"false"
Enable Kubernetes integration (create UserDeployment CRs).
KUBERNETES_ENABLED="true"
Set to false for local development without a K8s cluster.
KUBERNETES_NAMESPACE
string
default:"sandbox-%s"
Namespace pattern for user sandboxes. %s is replaced with user ID.
KUBERNETES_NAMESPACE="sandbox-%s"
Examples:
  • User user-123 → namespace sandbox-user-123
  • User alice → namespace sandbox-alice
KUBERNETES_RECONCILE_INTERVAL
duration
default:"30s"
Interval for reconciling deployment status from Kubernetes.
KUBERNETES_RECONCILE_INTERVAL="30s"

Session Configuration

SESSION_BACKEND
string
default:"memory"
Session storage backend. Options: memory, postgres, redis.
SESSION_BACKEND="postgres"

memory

In-memory storage (lost on restart).Use for: Local developmentDon’t use: Production

postgres

PostgreSQL-backed sessions.Use for: Production (shared database)Requires: DATABASE_DSN

redis

Redis-backed sessions.Use for: Production (separate cache)Requires: REDIS_ADDR
SESSION_DURATION
duration
default:"24h"
Session lifetime before automatic expiration.
SESSION_DURATION="24h"
Supports: 1h, 24h, 168h (7 days), etc.
Cookie name for session identifier.
SESSION_COOKIE_NAME="session_id"
SESSION_DURATION
duration
default:"24h"
Session lifetime before expiration.
SESSION_DURATION="24h"
SESSION_CLEANUP_INTERVAL
duration
default:"1h"
Interval for cleaning up expired sessions (memory and PostgreSQL backends only).
SESSION_CLEANUP_INTERVAL="1h"
Cookie domain for session cookies. Useful for subdomain sharing.
SESSION_COOKIE_DOMAIN=".example.com"
Cookie name for OAuth state parameter.
STATE_COOKIE_NAME="oauth_state"
Require HTTPS for cookies. Enable in production.
COOKIE_SECURE="true"
REDIS_ADDR
string
Redis server address. Required when SESSION_BACKEND=redis.
REDIS_ADDR="localhost:6379"

Secrets Backend

k8s-scheduler supports three secrets backends for user/template/deployment secrets:
SECRETS_BACKEND
string
default:"database"
Secrets storage backend. Options: database, vault, aws.
SECRETS_BACKEND="vault"

database

Encrypted secrets in PostgreSQL.Requires: SECRETS_ENCRYPTION_KEYUse for: Simple deployments

vault

HashiCorp Vault KV v2.Requires: VAULT_ADDR, VAULT_TOKENUse for: Production (recommended)

aws

AWS Secrets Manager.Requires: AWS_REGION, IAM roleUse for: AWS-native deployments

Database Backend

SECRETS_ENCRYPTION_KEY
string
Base64-encoded 32-byte encryption key. Required when SECRETS_BACKEND=database.
# Generate a key
openssl rand -base64 32

SECRETS_ENCRYPTION_KEY="your-base64-encoded-key"
Store this key securely. Losing it means all secrets become unrecoverable.

Vault Backend

VAULT_ADDR
string
Vault server address.
VAULT_ADDR="http://vault.vault.svc.cluster.local:8200"
VAULT_TOKEN
string
Vault access token.
VAULT_TOKEN="hvs.your-vault-token"
VAULT_TOKEN_FILE
string
Path to Vault token file (for Vault Agent auto-refresh).
VAULT_TOKEN_FILE="/vault/secrets/token"
Preferred over VAULT_TOKEN in production.
VAULT_MOUNT_PATH
string
default:"secret"
Vault KV mount path.
VAULT_MOUNT_PATH="secret"

AWS Backend

AWS_REGION
string
AWS region for Secrets Manager.
AWS_REGION="us-east-1"
AWS credentials are auto-discovered from:
  • Instance metadata (IAM role)
  • Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
  • ~/.aws/credentials

Optional Integrations

Billing (Stripe)

BILLING_ENABLED
boolean
default:"false"
Enable Stripe billing integration.
BILLING_ENABLED="true"
STRIPE_API_KEY
string
Stripe secret key (sk_test_... or sk_live_...).
STRIPE_API_KEY="sk_live_..."
STRIPE_WEBHOOK_SECRET
string
Stripe webhook signing secret (whsec_...).
STRIPE_WEBHOOK_SECRET="whsec_..."
Used to verify webhook payloads from Stripe.

Email (Invitations)

EMAIL_PROVIDER
string
Email provider. Options: smtp, sendgrid.
EMAIL_PROVIDER="smtp"
SMTP_HOST
string
SMTP server hostname.
SMTP_HOST="smtp.gmail.com"
SMTP_PORT
string
default:"587"
SMTP server port.
SMTP_PORT="587"
SMTP_USERNAME
string
SMTP authentication username.
SMTP_USERNAME="[email protected]"
SMTP_PASSWORD
string
SMTP authentication password.
SMTP_PASSWORD="your-password"
SENDGRID_API_KEY
string
SendGrid API key. Required when EMAIL_PROVIDER=sendgrid.
SENDGRID_API_KEY="SG.your-api-key"

AI (Template Generation)

ANTHROPIC_API_KEY
string
Anthropic API key for AI-assisted template generation.
ANTHROPIC_API_KEY="sk-ant-..."
Optional. Enables AI template generation feature in the UI.

Operator Configuration

The operator (cmd/operator) manages UserDeployment custom resources.

Environment Variables

DEPLOYMENT_DOMAIN
string
required
Base domain for ingress routing.
DEPLOYMENT_DOMAIN="example.com"
Must match the server’s DEPLOYMENT_DOMAIN.
CLUSTER_SECRET_STORE
string
ClusterSecretStore name for External Secrets Operator.
CLUSTER_SECRET_STORE="vault-backend"
If empty, the operator skips creating ExternalSecret resources.
DISABLE_NETWORK_POLICIES
boolean
default:"false"
Skip NetworkPolicy creation (for local development).
DISABLE_NETWORK_POLICIES="true"
Useful for clusters without NetworkPolicy support (e.g., kind without Calico).
WATCH_NAMESPACE
string
Namespace to watch for UserDeployments. Empty = all namespaces.
WATCH_NAMESPACE=""

Configuration Examples

Production (Vault + PostgreSQL)

.env
# Database
DATABASE_DSN="postgres://user:[email protected]:5432/scheduler?sslmode=require"

# OAuth
GOOGLE_CLIENT_ID="prod-client.apps.googleusercontent.com"
GOOGLE_CLIENT_SECRET="prod-secret"
BASE_URL="https://app.example.com"
GOOGLE_REDIRECT_URL="https://app.example.com/oauth2/callback"

# Domain
DEPLOYMENT_DOMAIN="example.com"

# Kubernetes
KUBERNETES_ENABLED="true"
KUBERNETES_NAMESPACE="sandbox-%s"

# Secrets
SECRETS_BACKEND="vault"
VAULT_ADDR="http://vault.vault.svc.cluster.local:8200"
VAULT_TOKEN_FILE="/vault/secrets/token"

# Session
SESSION_BACKEND="postgres"
SESSION_DURATION="24h"
COOKIE_SECURE="true"

# Server
REACT_UI="true"
SERVER_ADDR=":8081"

# Billing
BILLING_ENABLED="true"
STRIPE_API_KEY="sk_live_..."
STRIPE_WEBHOOK_SECRET="whsec_..."

# Email
EMAIL_PROVIDER="smtp"
SMTP_HOST="smtp.sendgrid.net"
SMTP_PORT="587"
SMTP_USERNAME="apikey"
SMTP_PASSWORD="SG.your-api-key"

Local Development

.env
# Database (local PostgreSQL)
DATABASE_DSN="postgres://postgres:postgres@localhost:5432/scheduler?sslmode=disable"

# Dev mode (skip OAuth)
DEV_MODE="true"
BASE_URL="http://localhost:8081"

# Domain
DEPLOYMENT_DOMAIN="localhost"

# Disable Kubernetes
KUBERNETES_ENABLED="false"

# Secrets (database)
SECRETS_BACKEND="database"
SECRETS_ENCRYPTION_KEY="your-base64-key"

# Session (in-memory)
SESSION_BACKEND="memory"

# Server
REACT_UI="true"
SERVER_ADDR=":8081"

AWS Deployment (No Vault)

.env
# Database (RDS)
DATABASE_DSN="postgres://user:[email protected]:5432/scheduler?sslmode=require"

# OAuth
GOOGLE_CLIENT_ID="prod-client.apps.googleusercontent.com"
GOOGLE_CLIENT_SECRET="prod-secret"
BASE_URL="https://app.example.com"
GOOGLE_REDIRECT_URL="https://app.example.com/oauth2/callback"

# Domain
DEPLOYMENT_DOMAIN="example.com"

# Kubernetes
KUBERNETES_ENABLED="true"
KUBERNETES_NAMESPACE="sandbox-%s"

# Secrets (AWS Secrets Manager)
SECRETS_BACKEND="aws"
AWS_REGION="us-east-1"

# Session (Redis)
SESSION_BACKEND="redis"
REDIS_ADDR="prod-redis.abc123.cache.amazonaws.com:6379"
SESSION_DURATION="24h"
COOKIE_SECURE="true"

# Server
REACT_UI="true"
SERVER_ADDR=":8081"

ConfigMap (Helm)

When deploying via Helm, non-secret configuration is stored in a ConfigMap:
charts/k8s-scheduler/templates/configmap-server.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: k8s-scheduler-server-config
  namespace: {{ .Release.Namespace }}
data:
  BASE_URL: "https://app.{{ .Values.domain }}"
  DEPLOYMENT_DOMAIN: "{{ .Values.domain }}"
  GOOGLE_REDIRECT_URL: "https://app.{{ .Values.domain }}/oauth2/callback"
  KUBERNETES_ENABLED: "true"
  KUBERNETES_NAMESPACE: "{{ .Values.kubernetes.namespaceTemplate }}"
  SESSION_BACKEND: "{{ .Values.session.backend }}"
  SESSION_DURATION: "{{ .Values.session.duration }}"
  COOKIE_SECURE: "{{ .Values.session.cookieSecure }}"
  SECRETS_BACKEND: "vault"
  VAULT_ADDR: "{{ .Values.vault.address }}"
  VAULT_MOUNT_PATH: "{{ .Values.vault.mountPath }}"
Secrets are injected via Vault Agent annotations on the server deployment.

Next Steps

Deployment Guide

Deploy k8s-scheduler to production

Dependencies

Learn about platform dependencies

Build docs developers (and LLMs) love