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.*):
- Readiness: 30s initial delay, 10s interval, 5s timeout, 5 failures → unready
- Liveness: 60s initial delay, 30s interval, 10s timeout, 3 failures → restart
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
| Cluster | Notes |
|---|---|
| EKS | Use 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. |
| AKS | Use the AGIC ingress (azure/application-gateway) or nginx ingress, plus cert-manager. Azure Files / Disk CSI for PVCs. |
| GKE | GKE Ingress (kubernetes.io/ingress.class: gce) integrates with Google-managed certs. Standard CSI for PD-SSD PVCs. |
| OpenShift | Use a Route instead of an Ingress, OR enable the Ingress controller. The default security context is compatible with restricted-v2 SCC. |
| k3s / on-prem | Traefik 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.