sam-gong-game

Local Deploy

Sam Gong (三公) — Local Deployment Manual

This guide walks you through setting up and running the Sam Gong multiplayer card game locally, from bare prerequisites to a fully functional Minikube deployment.


Table of Contents

  1. Prerequisites
  2. Clone and Setup
  3. Install Dependencies
  4. Environment Configuration
  5. Local Dev: Docker Compose (Postgres + Redis)
  6. Run Database Migrations
  7. Start Development Servers
  8. Run Tests
  9. Minikube Kubernetes Deployment
  10. Verify Health and Game Flow
  11. Troubleshooting

1. Prerequisites

Install the following tools before proceeding.

ToolVersionInstall
Node.js20 LTShttps://nodejs.org or nvm install 20
Docker DesktopLatesthttps://www.docker.com/products/docker-desktop
kubectl1.28+https://kubernetes.io/docs/tasks/tools/
minikube1.32+https://minikube.sigs.k8s.io/docs/start/
gitAnyhttps://git-scm.com

Verify installations:

node --version     # v20.x.x
docker --version   # Docker version 24.x.x
kubectl version    # v1.28+
minikube version   # v1.32+

2. Clone and Setup

git clone https://github.com/your-org/sam-gong-game.git
cd sam-gong-game

Generate RSA keys for JWT authentication:

mkdir -p keys
openssl genrsa -out keys/private.pem 4096
openssl rsa -in keys/private.pem -pubout -out keys/public.pem
chmod 600 keys/private.pem

Security: Never commit the keys/ directory to git. It is already listed in .gitignore.


3. Install Dependencies

npm install

This installs all backend and tooling dependencies. Frontend dependencies are managed separately in client/:

cd client && npm install && cd ..

4. Environment Configuration

Copy the template and fill in values:

cp .env.example .env

Edit .env with your local values. Key fields to update:

VariableDescriptionDefault (dev)
DB_PASSWORDPostgreSQL passworddev_password_change_me
REDIS_PASSWORDRedis passworddev_redis_password
JWT_PRIVATE_KEY_PATHPath to RS256 private key./keys/private.pem
JWT_PUBLIC_KEY_PATHPath to RS256 public key./keys/public.pem
OTP_API_KEYOTP provider API key(required for registration)
ANTI_ADDICTION_ENABLEDTaiwan regulation enforcementtrue

For local development, all other defaults in .env.example are suitable.


5. Local Dev: Docker Compose

Start PostgreSQL 15 and Redis 7 using Docker Compose:

docker compose -f infra/docker-compose.yml up -d

Verify services are healthy:

docker compose -f infra/docker-compose.yml ps

Both postgres and redis should show status healthy.

To also start pgAdmin (database web UI at http://localhost:5050):

docker compose -f infra/docker-compose.yml --profile tools up -d

Stop services when done:

docker compose -f infra/docker-compose.yml down

To remove all data volumes (full reset):

docker compose -f infra/docker-compose.yml down -v

6. Run Database Migrations

Run schema migrations after the database is healthy:

npm run migrate

Verify the migration completed:

npm run migrate:status

To roll back the last migration:

npm run migrate:rollback

7. Start Development Servers

Open three terminal windows and run each server:

Terminal 1 — Colyseus Game Server (WebSocket, port 2567):

npm run dev:server

Terminal 2 — REST API Server (port 3000):

npm run dev:api

Terminal 3 — Frontend Client (port 3001 or Vite default):

npm run dev:client

Or, if your package.json has a combined dev script:

npm run dev

Access the application:


8. Run Tests

Unit tests (Jest):

npm test

Tests with coverage:

npm run test:coverage

End-to-end tests (Playwright):

Ensure all three dev servers are running, then:

npm run test:e2e

Watch mode (for TDD):

npm run test:watch

9. Minikube Kubernetes Deployment

9.1 Start Minikube

minikube start --cpus=4 --memory=8192 --disk-size=30g

Enable required addons:

minikube addons enable ingress
minikube addons enable metrics-server

9.2 Point Docker to Minikube's Docker Daemon

This allows you to build images directly into Minikube without pushing to a registry:

eval $(minikube docker-env)

On Windows (PowerShell): & minikube -p minikube docker-env | Invoke-Expression

9.3 Build Docker Images

Build all three images into Minikube's Docker:

docker build -f infra/Dockerfile.server -t sam-gong-server:latest .
docker build -f infra/Dockerfile.api -t sam-gong-api:latest .
docker build -f infra/Dockerfile.client -t sam-gong-client:latest client/

9.4 Create Namespace and Apply Secrets

kubectl apply -f infra/k8s/namespace.yaml

Create the secret with real values (do NOT commit this):

kubectl create secret generic sam-gong-secrets \
  --namespace=sam-gong \
  --from-literal=DB_PASSWORD='your_db_password' \
  --from-literal=JWT_PRIVATE_KEY="$(cat keys/private.pem)" \
  --from-literal=JWT_PUBLIC_KEY="$(cat keys/public.pem)" \
  --from-literal=REDIS_PASSWORD='your_redis_password' \
  --from-literal=OTP_API_KEY='your_otp_key'

Production: Use Sealed Secrets or External Secrets Operator instead. See infra/k8s/secret.yaml for instructions.

9.5 Apply All Kubernetes Manifests

Apply manifests in order:

kubectl apply -f infra/k8s/configmap.yaml
kubectl apply -f infra/k8s/postgres-statefulset.yaml
kubectl apply -f infra/k8s/redis-statefulset.yaml

Wait for databases to be ready:

kubectl wait --namespace=sam-gong \
  --for=condition=ready pod \
  --selector=app=postgres \
  --timeout=120s

kubectl wait --namespace=sam-gong \
  --for=condition=ready pod \
  --selector=app=redis \
  --timeout=60s

Apply application manifests:

kubectl apply -f infra/k8s/deployment-server.yaml
kubectl apply -f infra/k8s/deployment-api.yaml
kubectl apply -f infra/k8s/deployment-client.yaml
kubectl apply -f infra/k8s/service-server.yaml
kubectl apply -f infra/k8s/service-api.yaml
kubectl apply -f infra/k8s/service-client.yaml
kubectl apply -f infra/k8s/ingress.yaml
kubectl apply -f infra/k8s/hpa.yaml
kubectl apply -f infra/k8s/pdb.yaml
kubectl apply -f infra/k8s/networkpolicy.yaml

Or apply the entire directory at once:

kubectl apply -f infra/k8s/

9.6 Run Database Migrations in Cluster

kubectl run migrate --rm -it \
  --namespace=sam-gong \
  --image=sam-gong-api:latest \
  --restart=Never \
  --env-from=configmap/sam-gong-config \
  --env-from=secret/sam-gong-secrets \
  -- node dist/migrate.js

9.7 Get the Minikube IP

minikube ip

Add the IP to your /etc/hosts (or C:\Windows\System32\drivers\etc\hosts):

<minikube-ip>  sam-gong.local

Access the app at: http://sam-gong.local


10. Verify Health and Game Flow

10.1 Check Pod Status

kubectl get pods -n sam-gong

All pods should show Running with READY 1/1 (or higher for multi-replica).

10.2 Health Checks

# API health
curl http://sam-gong.local/api/health

# Game server health (via port-forward if ingress not set up)
kubectl port-forward -n sam-gong svc/sam-gong-server-service 2567:2567 &
curl http://localhost:2567/health

Expected response:

{"status": "ok", "timestamp": "..."}

10.3 Game Flow Smoke Test

  1. Open http://sam-gong.local in your browser
  2. Register a new account (OTP verification required if ANTI_ADDICTION_ENABLED=true)
  3. Log in and navigate to the lobby
  4. Create a new game room
  5. Open a second browser tab, log in with a different account, and join the room
  6. Verify cards are dealt and game progresses

10.4 WebSocket Connectivity Test

# Using wscat (install: npm install -g wscat)
wscat -c ws://sam-gong.local/colyseus

10.5 View Logs

# Game server logs
kubectl logs -n sam-gong -l app=sam-gong-server --tail=100 -f

# API logs
kubectl logs -n sam-gong -l app=sam-gong-api --tail=100 -f

# Database logs
kubectl logs -n sam-gong postgres-0 --tail=50

11. Troubleshooting

Pods stuck in Pending

kubectl describe pod <pod-name> -n sam-gong

Common causes:

For local dev, patch StatefulSet storage class:

# Edit postgres-statefulset.yaml: change storageClassName: fast-ssd → standard
kubectl apply -f infra/k8s/postgres-statefulset.yaml

CrashLoopBackOff

kubectl logs -n sam-gong <pod-name> --previous

Common causes:

Database connection refused

# Test connectivity from inside the cluster
kubectl run pg-test --rm -it \
  --namespace=sam-gong \
  --image=postgres:15-alpine \
  --restart=Never \
  -- psql -h postgres-service -U sam_gong_app -d sam_gong

Redis authentication failure

kubectl run redis-test --rm -it \
  --namespace=sam-gong \
  --image=redis:7-alpine \
  --restart=Never \
  -- redis-cli -h redis-service -a <your_redis_password> ping

WebSocket 101 upgrade not working

Ensure the ingress addon is enabled and the upgrade annotations are applied:

kubectl get ingress -n sam-gong -o yaml | grep -A5 "proxy-read-timeout"

If annotations are missing, re-apply the ingress manifest:

kubectl apply -f infra/k8s/ingress.yaml

Minikube reset

To completely reset the Minikube cluster and start fresh:

minikube delete
minikube start --cpus=4 --memory=8192

Check resource quota usage

kubectl describe resourcequota sam-gong-quota -n sam-gong

HPA not scaling

kubectl describe hpa -n sam-gong

If metrics are unavailable, ensure metrics-server is enabled:

minikube addons enable metrics-server

Quick Reference

CommandDescription
docker compose -f infra/docker-compose.yml up -dStart local databases
npm run devStart all dev servers
npm testRun unit tests
npm run test:e2eRun e2e tests
minikube startStart local k8s cluster
eval $(minikube docker-env)Point Docker to Minikube
kubectl apply -f infra/k8s/Deploy to Minikube
kubectl get pods -n sam-gongCheck pod status
minikube tunnelExpose LoadBalancer services locally
minikube dashboardOpen k8s dashboard in browser