Chess is a Java chess project with two interfaces:
- A Swing desktop app.
- A Spring Boot + React web app for browser play, Stockfish review, and self-hosting.
The web version is designed to run on a home Ubuntu server behind Cloudflare Tunnel.
- Play human vs human in the browser.
- Play against Stockfish with configurable side and Elo.
- Import PGN text or
.pgn/.txtfiles for review. - Run game review with Stockfish best moves, move classifications, arrows, eval graph, and accuracy.
- Anonymous per-browser sessions using
localStorage, so different visitors do not share the same board. - Mobile-friendly board layout with touch scroll locking while moving pieces.
- Timer starts after White makes the first move, not when the game is created.
Local development:
- Java 17+
- Maven 3.9+
- Node.js 20+
- Stockfish binary for your OS
Server deployment:
- Ubuntu with Docker and Docker Compose plugin
- Cloudflare Tunnel, if exposing through
chess.patreek.no
Start the Spring Boot backend from the project root:
cd /Users/patrik/Documents/Prosjekter/Chess
./run-web.shThe backend runs on:
http://localhost:8081
In another terminal, start the React/Vite frontend:
cd /Users/patrik/Documents/Prosjekter/Chess/frontend
npm run devOpen:
http://localhost:3000
Vite proxies /api and /ws to Spring Boot on port 8081.
run-web.sh sets STOCKFISH_PATH to the bundled local path:
/Users/patrik/Documents/Prosjekter/Chess/engines/stockfish/stockfish-macos-m1-apple-siliconTo use another Stockfish binary:
export STOCKFISH_PATH=/absolute/path/to/stockfish
./run-web.shBuild and run the Swing version:
mvn clean package
java -cp target/classes main.MainOr use:
./run.shDeployment is split between GitHub Actions (build) and the server (pull and run). Nothing is built on the server, and GitHub Actions never connects to it.
.github/workflows/ci.yml runs on every pull request and every push to main:
- Backend tests (
mvn -B test). - Frontend production build.
- Docker image build. On pushes to
mainthe image is pushed to GHCR (authenticated withGITHUB_TOKEN) as:ghcr.io/pthormodsen/chess:latestghcr.io/pthormodsen/chess:<short-sha>(7-character commit SHA, e.g.8c59db7)
The image builds the React frontend, packages the Spring Boot app, and installs Stockfish at /usr/games/stockfish.
The project is checked out on docker01 at:
/home/pmt/docker/Chess
The checkout only provides docker-compose.yml and the Makefile. Compose runs ghcr.io/pthormodsen/chess:${CHESS_IMAGE_TAG:-latest}.
If the GHCR package is private, log in once on the server with a personal access token that has read:packages:
echo <token> | docker login ghcr.io -u pthormodsen --password-stdinOn a development machine:
make dev # backend on :8081 + Vite with hot reload on http://localhost:3000 (compose.dev.yml)
make dev-down # stop the dev containers
make test # backend tests
make build # build the production image as ghcr.io/pthormodsen/chess:localFrontend edits hot reload. Backend changes need make dev again, which rebuilds the backend image. To run without Docker, use ./run-web.sh plus npm run dev in frontend/ (see Local Development).
On the server, from /home/pmt/docker/Chess:
make deploy # git pull --ff-only, then make prod, then docker compose ps
make prod # docker compose pull && docker compose up -d
make prod-down # docker compose down
make rollback TAG=<sha> # pull and run ghcr.io/pthormodsen/chess:<sha>
make restart # restart the running container
make logs # follow container logs
make ps # docker compose ps (also: make status)A rollback lasts until the next make deploy or make prod, both of which return to latest. To find a SHA, use git log --oneline or the package page on GitHub.
git push origin main # wait for the CI workflow to finish and publish the image
ssh pmt@docker01
cd /home/pmt/docker/Chess
make deployNo host port is published. The chess-web container joins the external cloudflare Docker network, and cloudflared reaches it there. The compose file also sets memory, CPU and PID limits, log rotation, and STOCKFISH_PATH=/usr/games/stockfish.
The tunnel's public hostname for chess.patreek.no should point to:
http://chess-web:8081
The DNS record in Cloudflare should be:
Type: CNAME
Name: chess
Target: <tunnel-id>.cfargotunnel.com
Proxy status: Proxied
TTL: Auto
Check the deployed app:
curl -I https://chess.patreek.noIn the web app:
- Paste PGN into Import PGN, or upload a
.pgn/.txtfile. - Click Import for review.
- Click Analyze.
- Use Previous / Next, or keyboard arrow keys on desktop, to step through the review.
The backend parses SAN PGN with chesslib, converts it to UCI, then replays the moves through the Java board validation.
The current web app uses anonymous per-browser sessions. Each browser stores a generated session id in localStorage and sends it as X-Chess-Session.
This prevents public users from sharing the same active game, but it is not a full account system. Real accounts, saved games, login, and persistent review history would require adding a database and authentication.
If the deployed site does not update, first confirm that the CI run on main succeeded and pushed the image. Then:
cd /home/pmt/docker/Chess
make deploy
make logsIf the browser says DNS cannot resolve:
dig +short chess.patreek.noIf analysis fails, confirm Stockfish exists inside the container:
docker exec -it chess-web which stockfish
docker exec -it chess-web /usr/games/stockfish- Stockfish runs server-side. Any phone or computer using
chess.patreek.nosends requests to the Ubuntu server; it does not need Stockfish installed locally. - The server container uses Java 17.
- Local development uses the Mac Stockfish binary configured in
run-web.sh.