Files
caro/docs/deployment-guide.md
tiennm99 cbad690565 docs,chore: single-port 1999 websocket protobuf
Phase 05 — sync infrastructure and documentation with the typed-protobuf
refactor:
- docker-compose.yml: drop 1024/1025 mappings, single "1999:1999"
- server/Dockerfile: EXPOSE 1999, -p 1999 entrypoint
- README.md: rewrite transport description, architecture diagram, protocol
  section, server options, project structure, add proto:gen script note
- docs/project-overview.md: update transport + dependencies sections
- docs/system-architecture.md: rewrite diagrams + pipeline + file inventory
  for the WebSocket-only typed-dispatch path
- docs/codebase-summary.md: refresh file tree, java package inventory,
  gradle deps, vite deps, networking and game-flow sections
- docs/deployment-guide.md: single-port walkthrough for local / docker /
  systemd / nginx; remove all 1024/1025 firewall and troubleshooting
- docs/code-standards.md: replace dead ServerEventListener_CODE_* class-name
  example, fix sample ws:// URL to port 1999
2026-04-11 08:33:46 +07:00

8.7 KiB

Deployment Guide

Prerequisites

Required

  • Java 25 (LTS) — build + runtime
  • Gradle — NOT required. The project commits the Gradle wrapper (./server/gradlew) which pins Gradle 9.2.1 and auto-downloads if needed.
  • Node.js 22+ — for the client (development only; not needed if using Docker)
    • Verify: node --version

Optional

  • Git — for cloning repository
  • Docker + Docker Compose — recommended for quick start and production
  • Nginx/Apache — reverse proxy (optional)

Quick Start (Docker Compose)

Fastest path — single command:

docker compose up --build -d

Then:

  • Client: http://localhost:8080/
  • Server WebSocket: ws://localhost:1999/ratel (binary typed protobuf)

Stop:

docker compose down

What's running:

  • caro-server — Java 25 server, listens on port 1999 (WebSocket only at /ratel)
  • caro-client — Nginx serving the built Vite bundle on host port 8080

Storage: in-memory only; games are not persisted. Restarting clears all rooms.

Logs:

docker compose logs -f caro-server
docker compose logs -f caro-client

Local Development Setup

1. Clone Repository

git clone https://github.com/tiennm99/caro.git
cd caro

2. Build the Server

./server/gradlew -p server clean build

On Windows: server\gradlew.bat -p server clean build.

Output:

  • server/build/libs/caro-server-0.0.1.jar — shaded fat jar (~20 MB)

Skip tests:

./server/gradlew -p server build -x test

Run only tests:

./server/gradlew -p server test

Tests: 29 GomokuHelperTest + 8 GomokuAITest (JUnit 5). All must pass.

3. Run the Server

java -jar server/build/libs/caro-server-0.0.1.jar

Output:

[INFO] Server listening on port 1999 (WebSocket)
[INFO] WebSocket endpoint: /ratel

What's running:

  • WebSocket port 1999 — Binary typed protobuf at /ratel

Connect via: ws://localhost:1999/ratel

4. Run the Client (Vite dev server)

In a new terminal:

cd client
npm install
npm run dev

Output:

  VITE v6.3.1  ready in 234 ms
  ➜  Local:   http://localhost:5173/

Open http://localhost:5173/ — Phaser 3 client with full game UI.


Production Deployment

Option A: Standalone JAR

java -jar caro-server-0.0.1.jar

Server defaults to port 1999 (WebSocket only).

For production:

  • Run in background: nohup java -jar caro-server-0.0.1.jar &
  • Or use systemd (see below)
  • Or container (Docker)

System requirements:

  • 512 MB RAM (minimum)
  • 1 GB RAM (recommended)
  • 100 MB disk
  • Java 25 runtime

Option B: Docker Container

The included server/Dockerfile is multi-stage:

  • Build stage: eclipse-temurin:25-jdk + committed Gradle wrapper (./server/gradlew)
  • Runtime stage: eclipse-temurin:25-jre-alpine

Build from repo root (context = .):

docker build -f server/Dockerfile -t caro-server:0.0.1 .

Run:

docker run -d --name caro-server \
  -p 1999:1999/tcp \
  caro-server:0.0.1

Option C: Linux Systemd Service

Create /etc/systemd/system/caro-server.service:

[Unit]
Description=Caro Gomoku Server
After=network.target

[Service]
Type=simple
User=gameserver
WorkingDirectory=/opt/caro
ExecStart=/usr/bin/java -jar /opt/caro/caro-server-0.0.1.jar
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable caro-server
sudo systemctl start caro-server
sudo systemctl status caro-server
sudo journalctl -u caro-server -f

Client Deployment

The client service in docker-compose.yml builds client/dist/ and serves it via Nginx on host port 8080. Already covered in the Quick Start above.

Option B: Static Hosting (Netlify, Vercel, AWS S3)

Build:

cd client
npm ci
npm run build

Output: client/dist/ (ready to deploy)

Netlify:

netlify deploy --prod --dir client/dist

Vercel:

vercel --prod client

AWS S3:

aws s3 sync client/dist/ s3://my-bucket/caro/ --delete

Option C: Nginx Reverse Proxy

Serve client + proxy WebSocket to server:

server {
    listen 80;
    server_name caro.example.com;

    # Static files (client)
    location / {
        root /var/www/caro;
        try_files $uri /index.html;
    }

    # WebSocket proxy to server
    location /ratel {
        proxy_pass http://localhost:1999;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}

Deploy:

cd client
npm run build
sudo cp -r dist/* /var/www/caro/
sudo systemctl restart nginx

Configuration & Options

Server Options

Server uses defaults. No command-line arguments to configure port. Default port is 1999.

To change port, modify SimpleServer.java and rebuild:

// Default: 1999
private static final int DEFAULT_PORT = 1999;

Client Configuration

Connection endpoint is in client/src/services/connection-service.js. By default the client connects to WS on window.location.hostname:1999/ratel. To override, edit that file and rebuild.


Running Tests

Java Unit Tests (JUnit 5)

./server/gradlew -p server clean test

Expected output:

GomokuHelperTest > testHorizontalWin() PASSED
... (29 tests)
GomokuAITest > testEasyAIReturnsValidMove() PASSED
... (8 tests)

BUILD SUCCESSFUL

Run a single test class:

./server/gradlew -p server test --tests GomokuHelperTest

Test report: server/build/reports/tests/test/index.html

Client Tests

No automated tests currently. Manual testing:

  1. Start server: java -jar server/build/libs/caro-server-0.0.1.jar
  2. Start client: cd client && npm run dev
  3. Open http://localhost:5173
  4. Create game, make moves, test AI, spectate

Monitoring & Maintenance

Server Health Check

# Check WebSocket port
netstat -an | grep 1999
# or
lsof -i :1999

# Test WebSocket
websocat ws://localhost:1999/ratel

Logs

  • Foreground: stdout
  • systemd: journalctl -u caro-server -f
  • Docker: docker compose logs -f caro-server

Performance Monitoring

top -p $(pgrep -f caro-server)
netstat -an | grep -c ESTABLISHED

Cleanup

  • Finished rooms are auto-cleaned by RoomClearTask after timeout
  • No manual cleanup required

Restart:

kill $(pgrep -f caro-server)
java -jar server/build/libs/caro-server-0.0.1.jar

Troubleshooting

Port Already in Use

lsof -i :1999
kill -9 <PID>

To use a different port, modify SimpleServer.java and rebuild.

Connection Refused

  1. Verify server is running: ps aux | grep caro-server
  2. Check firewall: sudo ufw allow 1999/tcp
  3. Test connectivity: nc -zv localhost 1999
  4. Docker: docker compose ps and docker compose port server

WebSocket Connection Fails

  1. WebSocket listens on port 1999 at path /ratel
  2. Ensure the client URL is correct: ws://host:1999/ratel
  3. Test directly: websocat ws://localhost:1999/ratel
  4. Verify server logs for request decoding errors

High Memory Usage

# Check concurrent clients
netstat -an | grep ESTABLISHED | wc -l

# Increase JVM heap
java -Xmx2g -jar caro-server-0.0.1.jar

# Restart to reclaim memory

Deployment Checklist

Before going live:

  • Java 25 installed and verified
  • ./server/gradlew -p server clean build passes (37 tests)
  • Server boots without errors
  • WebSocket port 1999 open (firewall)
  • Client built: npm --prefix client ci && npm --prefix client run build
  • Client connects to correct server endpoint
  • Manual test: create game, make moves, join room, spectate
  • Monitoring configured (logs, memory, CPU)
  • Backup JAR kept on hand

Updating & Patching

Update Server

  1. Back up current jar
  2. Pull + rebuild: git pull && ./server/gradlew -p server clean build
  3. Stop: kill $(pgrep -f caro-server)
  4. Start new jar: java -jar server/build/libs/caro-server-0.0.1.jar

Update Client

  1. git pull && npm --prefix client ci && npm --prefix client run build
  2. Deploy client/dist/ to hosting (or rebuild Docker image with docker compose build client)

Zero-Downtime Update

  1. Run new server on a different port (e.g. -p 9090)
  2. Migrate clients gradually (or wait for natural disconnect)
  3. Stop the old server

Performance Tuning

JVM Tuning

For production servers with 4+ GB RAM:

java -Xmx4g -XX:+UseG1GC -jar caro-server-0.0.1.jar

Java 25 defaults are already sensible; tune only if needed.