Skip to content

Environments ​

Three environments: your laptop, staging, and production.

EnvironmentWhereStatus
LocalDocker Compose + pnpm devWorking
StagingDOKS staging namespace, *.staging.playpalz.ggLive
ProductionDOKS default namespaceLive

Mobile preview builds still talk to production

The mobile preview profile builds with EXPO_PUBLIC_ENV=staging, but its EAS environment points at the production API and socket hosts. Point it at staging with eas env:set preview --name EXPO_PUBLIC_API_URL --value https://api.staging.playpalz.gg/api/v1 (and EXPO_PUBLIC_REALTIME_URL → https://socket.staging.playpalz.gg), then rebuild the preview app.

Local ​

ServicePortHow
PostgreSQL5432docker compose up -d db
Redis6379docker compose up -d redis (password playpalz)
LiveKit7880–7882docker compose up -d livekit (devkey / secret)
api4000pnpm --filter @playpals/api dev
esu4010pnpm --filter @playpals/esu dev
ogun—pnpm --filter @playpals/ogun dev
anansi4005pnpm --filter @playpals/anansi dev
web3000pnpm --filter @playpals/web dev
docs5173pnpm --filter @playpals/docs dev

pnpm dev starts api, esu, ogun, and mobile together; pnpm dev:all starts everything.

Third-party services degrade gracefully — without Spaces, Mux, RevenueCat, or Stripe credentials the app still runs; those features just do not work. Full setup: Local Setup.

Docker-based local ​

docker-compose.dev.yaml overlays live-reload containers for api, esu, and ogun on top of the base Compose stack:

bash
./start-dev.sh          # foreground, everything
./start-dev.sh -d api   # detached, one service

Slower than running natively, but eliminates environment drift between developers.

Production ​

Namespace default in the DOKS cluster.

ServiceReplicasStrategyIngress
web3RollingUpdatewww.playpalz.gg, playpalz.gg
api3RollingUpdateapi.playpalz.gg
esu2RollingUpdatesocket.playpalz.gg
ogun2 (HPA 2–8)RollingUpdate—
anansi1Recreate—
igdb-heartbeat1Recreate—
redis1 (StatefulSet + 5Gi PVC)——

The two singletons use Recreate rather than RollingUpdate deliberately: both are cron-driven, and briefly running two pods during a rollout risks a duplicate payout run or a duplicate sync.

anansi's Redis lock makes that safe in principle, but Recreate means never having to rely on it.

Staging Live ​

A full copy of the platform for testing changes before production, in the staging namespace of the same cluster. It runs the same Helm charts as production, with infra/helm/values/staging/.

ProductionStaging
Namespacedefaultstaging
Webwww.playpalz.gg, playpalz.ggstaging.playpalz.gg
APIapi.playpalz.ggapi.staging.playpalz.gg
Realtimesocket.playpalz.ggsocket.staging.playpalz.gg
Adminadmin.playpalz.ggadmin.staging.playpalz.gg
Docsdocs.playpalz.ggdocs.staging.playpalz.gg
ReplicasAs above1 each, no autoscaling
DatabaseManaged Postgres 15In-cluster Postgres 15 (playpalz-postgres, 10Gi volume)
Redis5Gi, 512 MB dataset1Gi, 256 MB dataset
Media / KYCplaypalz-media-production, playpalz-kycplaypalz-media-staging, playpalz-kyc-staging (own scoped keys)
StripeLive keys (once live)Test mode
RevenueCatProduction webhookSandbox purchases, its own webhook secret
MuxProduction environmentPlaceholder until a Mux development environment is set up: video uploads fail at Mux
LiveKit, IGDB—Shared with production
ENVproductionstaging: secrets still required, sandbox coin purchases accepted

What's different in staging:

  • Email: staging is set up without email, so it shouldn't send any to real people.
  • Socket.IO admin UI: off.
  • Data: the database starts from migrations plus the demo seed (pnpm db:seed).

Deploying: every service and the platform release take --env staging:

bash
TAG=$(git rev-parse --short HEAD)
./scripts/build-api.sh "$TAG"                     # images are shared with production…
./scripts/build-web.sh "$TAG" staging             # …except web, which bakes in the staging API URL
infra/helm/deploy.sh --env staging api "$TAG"
infra/helm/deploy.sh --env staging web "$TAG"     # deploys <tag>-staging

From CI/CD Phase 4, merging to master does this automatically, and production is promoted from the same images.

Re-seeding or resetting staging data. Postgres is only reachable inside the cluster, so seed through a port-forward:

bash
kubectl -n staging port-forward svc/playpalz-postgres 5433:5432 &
export DATABASE_URL="postgresql://playpalz:$(kubectl -n staging get secret database-credentials \
  -o jsonpath='{.data.DB_PASSWORD}' | base64 -d)@localhost:5433/playpalz"
cd packages/database && pnpm db:seed

To start over: helm uninstall playpalz-platform -n staging, delete the data-playpalz-postgres-0 PVC, then redeploy platform and api (whose hook re-runs every migration).

Mobile environments ​

Independent of the backend deployment, driven by EXPO_PUBLIC_ENV. Values live in the EAS environment named after each profile:

ProfileEnvAPIRealtime
developmentdevelopmentlocalhost:4000localhost:4010
previewstagingapi.playpalz.gg ⚠️socket.playpalz.gg ⚠️
productionproductionapi.playpalz.ggsocket.playpalz.gg

⚠️ Preview builds still talk to production; the staging backend exists, but the preview EAS environment hasn't been switched to it yet (see the warning at the top). See Builds & Releases.

Internal documentation — PlayPalz platform