Skip to main content

Kubernetes

The Shift Platform uses Kustomize for Kubernetes manifest management, with a base layer and per-environment overlays.

Kustomize Structure

k8s/
|-- kind-config.yml # Kind cluster config (port mapping, ingress-ready)
|-- base/ # Shared across all environments
| |-- kustomization.yml
| |-- namespace.yml # shift-platform namespace
| |-- configmap.yml # Base config (PORT, SHIFT_STORAGE, etc.)
| |-- deployment.yml # Gateway deployment (2 replicas, resource limits)
| |-- service.yml # ClusterIP 80 -> 3000
| |-- ingress.yml # Host-based routing
| +-- convex/
| |-- deployment.yml # Self-hosted Convex (port 3210, /version readiness)
| +-- service.yml # ClusterIP 3210
+-- overlays/
|-- dev/ # Local development (kind)
| |-- kustomization.yml
| |-- configmap-patch.yml # NODE_ENV=development, LOG_LEVEL=debug
| |-- deployment-patch.yml # 1 replica, lower resources
| +-- ingress-patch.yml # host: shift.lvh.me
|-- staging/ # Staging (staging.the-shift.dev)
| +-- ...
+-- prod/ # Production (app.the-shift.dev)
+-- ...

Base Resources

Namespace

All platform resources live in the shift-platform namespace (or shift-platform-staging for staging).

ConfigMap

The base ConfigMap sets default configuration values:

KeyDefault
PORT3000
SHIFT_STORAGEconvex
NODE_ENVproduction

Overlays patch environment-specific values (e.g., LOG_LEVEL=debug in dev).

Deployment

The gateway deployment runs the shift-platform image with:

  • 2 replicas (base, overridden per environment)
  • Resource requests and limits
  • Readiness probe on /healthz
  • Environment variables from the ConfigMap and Secrets

Service

A ClusterIP service maps port 80 to the gateway's port 3000.

Ingress

Host-based routing directs traffic to the gateway service. Each overlay sets the appropriate hostname and ingress class.

Convex Backend

A self-hosted Convex instance runs alongside the gateway:

  • Single replica deployment on port 3210
  • Readiness probe on /version
  • ClusterIP service exposing port 3210
  • The gateway connects to it via the in-cluster URL: http://convex-backend.shift-platform.svc.cluster.local:3210

Secrets

Sensitive values are stored in a Kubernetes secret (shift-platform-secrets) and injected as environment variables:

KeyDescription
GOOGLE_CLIENT_IDGoogle OAuth client ID
GOOGLE_CLIENT_SECRETGoogle OAuth client secret
GOOGLE_CLI_CLIENT_IDGoogle OAuth client ID (CLI)
GOOGLE_CLI_CLIENT_SECRETGoogle OAuth client secret (CLI)
SESSION_SECRETHMAC key for session cookies
SHIFT_API_KEYAPI key for scripts and agents

In local dev, secrets are injected from Doppler. In staging and production, they are managed via the CI/CD pipeline.

Applying Manifests

# Dev overlay (kind)
kubectl apply -k k8s/overlays/dev/

# Staging overlay
kubectl apply -k k8s/overlays/staging/

# Production overlay
kubectl apply -k k8s/overlays/prod/