Kubernetes & Helm

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_DOMAIN that 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

  1. Put the Compose installation in maintenance mode.
  2. Run a final PostgreSQL backup from the Compose host.
  3. Restore the dump into the Kubernetes PostgreSQL target.
  4. Copy plugins/ to the chart’s /app/plugins PVC.
  5. Copy uploaded assets to the chart’s /app/uploads PVC.
  6. If the public domain changes, run scripts/deployment/deactivate-license.sh from the old host before activating the new LICENSE_DOMAIN.
  7. 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
Try the live demo Pricing

Have questions about Poker Engine? We reply fast.