Kubernetes Deployment (Helm)

The bundled chart at helm/ targets any distribution (EKS, AKS, GKE, OpenShift, k3s, on-prem), one chart, per-environment values. Out of the box it renders a Deployment, a Service exposing both ports, an optional Ingress, a pre-install/pre-upgrade migration Job, and (for evaluation only) a single-node Postgres StatefulSet.

Evaluation install

helm install dbstudio ./helm \\
  --namespace dbstudio --create-namespace \\
  --set secrets.jwtSecret=$(openssl rand -hex 32) \\
  --set secrets.encryptionKey=$(openssl rand -hex 32) \\
  --set secrets.dbPassword=$(openssl rand -hex 16) \\
  --set ingress.enabled=true \\
  --set ingress.hosts[0].host=dbstudio.example.com

Production values

Provision secrets out-of-band (Sealed Secrets, External Secrets Operator, Vault, cloud secret store) and reference them via secrets.existingSecret; disable the bundled Postgres and point at managed Postgres; make the migration Job the single migration owner (migrateOnBoot: false on app pods).

overrides.yaml

secrets:
  existingSecret: dbstudio-prod-secrets    # you create this Secret separately

postgres:
  enabled: false                            # use managed Postgres instead

externalPostgres:
  host: my-rds.example.com
  port: 5432
  database: db_devops
  username: simcha
  # password comes from the Secret's DB_PASSWORD key

config:
  clientUrl: https://dbstudio.example.com
  nextPublicApiUrl: https://dbstudio.example.com/api
  migrateOnBoot: false                      # the migration Job is the single path

ingress:
  enabled: true
  className: nginx
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt
  hosts:
    - host: dbstudio.example.com
      paths:
        - path: /api
          pathType: Prefix
          servicePort: 5002
        - path: /
          pathType: Prefix
          servicePort: 3000
  tls:
    - secretName: dbstudio-tls
      hosts: [dbstudio.example.com]

resources:
  limits:
    cpu: "2"
    memory: 4Gi
  requests:
    cpu: "500m"
    memory: 1Gi

replicaCount: 2
helm install dbstudio ./helm \\
  --namespace dbstudio --create-namespace \\
  --values overrides.yaml

Required Secret keys

Whether the chart creates the Secret (secrets.jwtSecret / secrets.encryptionKey / secrets.dbPassword) or you supply secrets.existingSecret, the resolved Secret must carry JWT_SECRET (≥32), ENCRYPTION_KEY (≥32), and DB_PASSWORD. Licensing needs no Secret key, it verifies against the image's baked-in public key.

Migration Job

The chart renders a Helm-hooked Job ({release}-migrate, pre-install,pre-upgrade) running node dist/server/database/migrate.js against the configured Postgres; Helm blocks the app rollout until it succeeds. With migrateOnBoot: false on the Deployment this Job is the sole migration owner, the correct HA posture, since it eliminates the race of N replicas each attempting migration at boot and guarantees the rollout aborts on a migration failure (unlike the non-fatal on-boot path). Set migration.enabled=false only if you drive migrations from your own pipeline.

Probes

Readiness and liveness both hit GET /api/health on the API port. Because readiness returns 503 until the metadata DB is reachable, replicas are held out of the Service endpoints until they can actually serve. Defaults (tune via probes.readiness.* / probes.liveness.*):

HA and scheduling

The app is stateless, so replicaCount > 1 scales linearly; combine with a PodDisruptionBudget and topology-spread/anti-affinity across zones for real HA. Each replica opens its own Postgres pool (default 20), size max_connections or front with PgBouncer accordingly (see Production & Scaling). Rollouts use the chart's maxSurge/maxUnavailable; tini + the entrypoint's SIGTERM drain make pod termination graceful.

Pod security context

Defaults to runAsNonRoot: true, runAsUser: 1001, all capabilities dropped, privilege escalation disabled, compatible with the Pod Security Standards restricted profile (and OpenShift restricted-v2 SCC) unmodified.

Cloud-specific notes

ClusterNotes
EKSUse aws-load-balancer-controller for an ALB ingress; cert-manager with a letsencrypt ClusterIssuer for TLS; ebs-csi-driver for the bundled-Postgres PVC if you don't use external Postgres.
AKSUse the AGIC ingress (azure/application-gateway) or nginx ingress, plus cert-manager. Azure Files / Disk CSI for PVCs.
GKEGKE Ingress (kubernetes.io/ingress.class: gce) integrates with Google-managed certs. Standard CSI for PD-SSD PVCs.
OpenShiftUse a Route instead of an Ingress, OR enable the Ingress controller. The default security context is compatible with restricted-v2 SCC.
k3s / on-premTraefik ingress is built in; works out of the box with ingress.className: traefik. Use local-path StorageClass or your own CSI driver.

Upgrades

# Pull the new chart values + image tag
helm upgrade dbstudio ./helm \\
  --namespace dbstudio \\
  --values overrides.yaml \\
  --set image.tag=1.0.2

# Roll back if needed
helm history dbstudio -n dbstudio
helm rollback dbstudio <revision> -n dbstudio

Helm runs the migration Job during each upgrade (idempotent, re-applying done migrations is a no-op). The Deployment then rolls out new pods with the maxSurge / maxUnavailable defaults from the chart.