Environments
Three environments: your laptop, staging, and production.
| Environment | Where | Status |
|---|---|---|
| Local | Docker Compose + pnpm dev | Working |
| Staging | DOKS staging namespace, *.staging.playpalz.gg | Live |
| Production | DOKS default namespace | Live |
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
| Service | Port | How |
|---|---|---|
| PostgreSQL | 5432 | docker compose up -d db |
| Redis | 6379 | docker compose up -d redis (password playpalz) |
| LiveKit | 7880–7882 | docker compose up -d livekit (devkey / secret) |
| api | 4000 | pnpm --filter @playpals/api dev |
| esu | 4010 | pnpm --filter @playpals/esu dev |
| ogun | — | pnpm --filter @playpals/ogun dev |
| anansi | 4005 | pnpm --filter @playpals/anansi dev |
| web | 3000 | pnpm --filter @playpals/web dev |
| docs | 5173 | pnpm --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:
./start-dev.sh # foreground, everything
./start-dev.sh -d api # detached, one serviceSlower than running natively, but eliminates environment drift between developers.
Production
Namespace default in the DOKS cluster.
| Service | Replicas | Strategy | Ingress |
|---|---|---|---|
web | 3 | RollingUpdate | www.playpalz.gg, playpalz.gg |
api | 3 | RollingUpdate | api.playpalz.gg |
esu | 2 | RollingUpdate | socket.playpalz.gg |
ogun | 2 (HPA 2–8) | RollingUpdate | — |
anansi | 1 | Recreate | — |
igdb-heartbeat | 1 | Recreate | — |
redis | 1 (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/.
| Production | Staging | |
|---|---|---|
| Namespace | default | staging |
| Web | www.playpalz.gg, playpalz.gg | staging.playpalz.gg |
| API | api.playpalz.gg | api.staging.playpalz.gg |
| Realtime | socket.playpalz.gg | socket.staging.playpalz.gg |
| Admin | admin.playpalz.gg | admin.staging.playpalz.gg |
| Docs | docs.playpalz.gg | docs.staging.playpalz.gg |
| Replicas | As above | 1 each, no autoscaling |
| Database | Managed Postgres 15 | In-cluster Postgres 15 (playpalz-postgres, 10Gi volume) |
| Redis | 5Gi, 512 MB dataset | 1Gi, 256 MB dataset |
| Media / KYC | playpalz-media-production, playpalz-kyc | playpalz-media-staging, playpalz-kyc-staging (own scoped keys) |
| Stripe | Live keys (once live) | Test mode |
| RevenueCat | Production webhook | Sandbox purchases, its own webhook secret |
| Mux | Production environment | Placeholder until a Mux development environment is set up: video uploads fail at Mux |
| LiveKit, IGDB | — | Shared with production |
ENV | production | staging: 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:
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>-stagingFrom 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:
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:seedTo 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:
| Profile | Env | API | Realtime |
|---|---|---|---|
development | development | localhost:4000 | localhost:4010 |
preview | staging | api.playpalz.gg ⚠️ | socket.playpalz.gg ⚠️ |
production | production | api.playpalz.gg | socket.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.
