Poker Engine supports Kubernetes as an enterprise deployment option alongside the Docker Compose/WLM tarball path. The Helm chart lives at deploy/helm/poker-engine and deploys the existing monolithic image: REST API, Socket.io, and background workers run in one Node.js process on port 3000.
Prerequisites
- Kubernetes 1.27 or newer.
- Helm 3.14 or newer.
- A production Poker Engine image tag, for example
ghcr.io/aronprins/poker-engine:0.0.42. - PostgreSQL 16 and Redis 7, either through managed services or the optional Bitnami subcharts.
- An ingress controller with WebSocket support. The defaults target ingress-nginx.
- A valid WLM license key and a
LICENSE_DOMAINthat matches the canonical ingress host.
Install With External PostgreSQL and Redis
Create a values file:
image:
repository: ghcr.io/aronprins/poker-engine
tag: 0.0.42
config:
corsOrigins: https://poker.example.com
secrets:
databaseUrl: postgresql://poker:REDACTED@db.example.com:5432/poker_engine?schema=public&sslmode=require
redisUrl: redis://:REDACTED@redis.example.com:6379/0
jwtAccessSecret: "<64-plus chars>"
jwtRefreshSecret: "<64-plus chars>"
jwt2faTempSecret: "<64-plus chars>"
csrfSecret: "<32-plus chars>"
encryptionKey: "<64 hex chars>"
adminInitialPassword: "<unique first-install admin password>"
licenseKey: "<wlm license>"
licenseDomain: poker.example.com
ingress:
enabled: true
hosts:
- host: poker.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: poker-engine-tls
hosts:
- poker.example.com
secrets.adminInitialPassword creates the admin account on an empty database. The chart doesn’t enforce it, but the app refuses to start on an empty database without it. It must be 12-128 characters with upper and lower case, a number and a special character.
Install from the published chart:
helm upgrade --install poker-engine oci://ghcr.io/aronprins/charts/poker-engine \
--version 0.0.42 \
--namespace poker-engine --create-namespace \
-f values.production.yaml
helm test poker-engine -n poker-engine
Or from a source checkout:
helm dependency build deploy/helm/poker-engine
helm upgrade --install poker-engine deploy/helm/poker-engine \
--namespace poker-engine --create-namespace \
-f values.production.yaml
Secrets Options
For GitOps, sealed-secrets, SOPS, or External Secrets Operator, set:
secrets:
existingSecret: poker-engine-runtime
The Secret is consumed with envFrom, so its keys must be the exact environment variable names: DATABASE_URL, JWT_ACCESS_SECRET, JWT_REFRESH_SECRET, JWT_2FA_TEMP_SECRET, CSRF_SECRET, ENCRYPTION_KEY, LICENSE_KEY and LICENSE_DOMAIN. Add REDIS_URL when Redis is used and ADMIN_INITIAL_PASSWORD for the first install. SENTRY_DSN and METRICS_SCRAPE_TOKEN are optional.
Bundled PostgreSQL and Redis
The chart can deploy optional Bitnami PostgreSQL and Redis subcharts for development and small internal installs:
postgresql:
enabled: true
auth:
password: "<strong password>"
redis:
enabled: true
auth:
password: "<strong password>"
Managed PostgreSQL and Redis remain the recommended production option because enterprise clusters normally use managed backups, point-in-time restore, and separate database maintenance windows.
Migrations and Upgrades
Self-updates are disabled because the chart sets DEPLOYMENT_MODE=kubernetes, so upgrade with helm upgrade. Every install and upgrade runs a Helm hook Job:
npx prisma migrate deploy
The application Deployment is not updated until the migration Job succeeds. Failed migrations abort the Helm release. The chart never runs prisma migrate reset or --force-reset.
Upgrade:
helm upgrade poker-engine deploy/helm/poker-engine \
--namespace poker-engine \
-f values.production.yaml \
--set image.tag=0.0.43
Rollback:
helm rollback poker-engine <revision> -n poker-engine
Database migrations are not automatically reverted by helm rollback. Restore from a verified database backup when rolling back across incompatible schema changes.
Live Session Migration
Set config.liveMigrationEnabled: "true" to enable planned zero-disconnect table migration for single-replica upgrades:
config:
liveMigrationEnabled: "true"
liveMigrationDrainWindowMs: "90000"
liveMigrationGraceMs: "10000"
liveMigration:
preStopSleepSeconds: 95
terminationGracePeriodSeconds: 120
When enabled, the pod preStop hook sends SIGUSR2 to the app. The app stops readiness, rejects new seats, stores disconnect records for connected sockets, pauses active tables at the drain deadline, persists private table snapshots, and exits. The next pod restores pending snapshots before accepting traffic. Keep terminationGracePeriodSeconds greater than the drain window plus a small buffer.
Ingress, TLS, and WebSockets
The default ingress annotations set one-hour proxy read/send timeouts and cookie affinity for Socket.io:
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/affinity: cookie
nginx.ingress.kubernetes.io/session-cookie-name: pe-affinity
TLS terminates at ingress. TRUST_PROXY defaults to 1 in the chart so Express sees client IPs and HTTPS state correctly behind the ingress controller.
Licensing
Runtime licensing is unchanged from Docker Compose. LICENSE_KEY and LICENSE_DOMAIN are read from Kubernetes Secret data, and the app validates against the WLM API. LICENSE_DOMAIN must be the bare domain players use, such as poker.example.com.
If NetworkPolicy is enabled, the app needs HTTPS egress to www.onlinepokerscript.com for WLM license checks and to any payment providers configured in the Admin Panel. networkPolicy.allowHttpsEgress allows port 443 and is on by default. Add other destinations, such as an SMTP server, with networkPolicy.extraEgress.
Scaling Status
The app defaults to one replica because live table state is currently in process memory. Redis exists for Socket.io adapter support, but it is not yet authoritative table storage. Multiple app replicas can split gameplay state. The chart blocks replicaCount > 1 and HPA unless gameState.multiReplicaAcknowledged=true is set, which is meant only for explicitly accepted non-gameplay scenarios.
Safe today:
- Vertical scaling with CPU/memory resources.
- Managed PostgreSQL and Redis.
- Sticky ingress sessions.
- One app pod with persistent plugin/upload PVCs.
Blocked until future game-state work:
- General horizontal scaling for active gameplay.
- Splitting REST, WebSocket, and worker processes.
- Multi-replica live hand migration. Planned single-replica zero-disconnect redeploys are supported when
config.liveMigrationEnabled="true".
Backups
Set backup.enabled: true to run a pg_dump CronJob to a PVC, on the backup.schedule and backup.retentionDays values. Managed PostgreSQL snapshots remain the recommended production option.
Compose to Kubernetes Migration
- Put the Compose installation in maintenance mode.
- Run a final PostgreSQL backup from the Compose host.
- Restore the dump into the Kubernetes PostgreSQL target.
- Copy
plugins/to the chart’s/app/pluginsPVC. - Copy uploaded assets to the chart’s
/app/uploadsPVC. - If the public domain changes, run
scripts/deployment/deactivate-license.shfrom the old host before activating the newLICENSE_DOMAIN. - Install the chart and verify
/health/ready,helm test, admin login, lobby load, and a Socket.io gameplay smoke test through ingress.
Validation
From a source checkout:
scripts/deployment/validate-helm-chart.sh
scripts/deployment/check-helm-env-parity.sh