Deploy to a DigitalOcean droplet: wizwar is on the internet

Production shape: one Node process serves the built client and the
websocket on a single port (SPA fallback, same-origin wss in the
client), with Caddy terminating auto-TLS in front. The droplet
(nyc3, $6/mo) runs it under systemd as an unprivileged user with room
files on the persistent disk at /var/lib/wizwar/rooms — deploys and
reboots cannot eat a game. deploy/ carries the one-time droplet setup
script, the systemd unit, the Caddyfile, an everyday deploy script
(build locally, rsync, install, restart), and a README. Live at
https://wizwar.104.236.96.198.sslip.io via sslip.io, so TLS needed no
DNS setup at all. Verified over the real internet: room created,
second player joined, expansion game started, hands dealt, room file
persisted. Fixed en route: rsync's unanchored "data" exclude was
stripping the engine's card database, and tsx must ship (it is the
runtime).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Eric Wagoner
2026-08-16 00:54:15 -04:00
co-authored by Claude Fable 5
parent 002dbd2547
commit 23bdd009a0
7 changed files with 145 additions and 3 deletions
+6
View File
@@ -0,0 +1,6 @@
# Caddy terminates TLS (automatic certificates) and proxies to the game.
# Replace the hostname with your domain, or use the sslip.io form
# (wizwar.<droplet-ip>.sslip.io) for zero DNS setup.
{$WIZWAR_HOST}
reverse_proxy localhost:8787
+36
View File
@@ -0,0 +1,36 @@
# Deploying Wiz-War
The game runs on a single DigitalOcean droplet: one Node process serves the
built client and the websocket on port 8787; Caddy in front terminates TLS
with automatic certificates. Room files persist in /var/lib/wizwar/rooms and
survive deploys and reboots.
## Current production
- Droplet: `wizwar` (nyc3, s-1vcpu-1gb, tag `wizwar`), IP 104.236.96.198
- URL: https://wizwar.104.236.96.198.sslip.io
(sslip.io resolves the embedded IP — zero DNS setup. To use a real domain,
point an A record at the droplet and update /etc/caddy/Caddyfile.)
## Everyday deploys
deploy/deploy.sh 104.236.96.198
Builds the client locally, rsyncs the repo (minus node_modules, /data, .git,
research), installs dependencies on the droplet, and restarts the service.
Games in progress survive: state lives in room files, and clients reconnect
automatically.
## New droplet from scratch
1. `doctl compute droplet create wizwar --region nyc3 --size s-1vcpu-1gb \
--image ubuntu-24-04-x64 --ssh-keys <your-key-ids> --tag-name wizwar --wait`
2. `scp deploy/setup-droplet.sh root@<ip>:/root/ && ssh root@<ip> \
"bash /root/setup-droplet.sh wizwar.<ip>.sslip.io"`
3. `deploy/deploy.sh <ip>`
## Operations
- Logs: `ssh root@<ip> journalctl -u wizwar -f`
- Restart: `ssh root@<ip> systemctl restart wizwar`
- Backup games: `scp -r root@<ip>:/var/lib/wizwar/rooms ./rooms-backup`
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env bash
# Build locally, push to the droplet, restart. Usage: deploy/deploy.sh <droplet-ip>
set -euo pipefail
HOST="${1:?usage: deploy.sh <droplet-ip-or-host>}"
npm run build --workspace=@wizwar/web
rsync -az --delete \
--exclude node_modules --exclude /data --exclude .git --exclude research \
./ "root@$HOST:/opt/wizwar/"
ssh "root@$HOST" '
cd /opt/wizwar && npm install --no-audit --no-fund
chown -R wizwar:wizwar /opt/wizwar
cp /opt/wizwar/deploy/wizwar.service /etc/systemd/system/wizwar.service
systemctl daemon-reload
systemctl enable --now wizwar
systemctl restart wizwar
systemctl --no-pager -l status wizwar | head -5
'
echo "deployed."
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env bash
# One-time droplet setup. Run ON the droplet as root:
# bash setup-droplet.sh wizwar.<droplet-ip>.sslip.io
set -euo pipefail
HOST="${1:?usage: setup-droplet.sh <hostname>}"
apt-get update -q
apt-get install -qy curl git rsync
# Node 22
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
apt-get install -qy nodejs
# Caddy (auto-HTTPS)
apt-get install -qy debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
| gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
| tee /etc/apt/sources.list.d/caddy-stable.list
apt-get update -q && apt-get install -qy caddy
# App user + directories
id -u wizwar &>/dev/null || useradd -r -m -d /opt/wizwar-home wizwar
mkdir -p /opt/wizwar /var/lib/wizwar/rooms
chown -R wizwar:wizwar /opt/wizwar /var/lib/wizwar
# Caddy vhost
printf '%s\n\nreverse_proxy localhost:8787\n' "$HOST" > /etc/caddy/Caddyfile
systemctl reload caddy
echo "droplet ready — now run deploy.sh from your machine"
+17
View File
@@ -0,0 +1,17 @@
[Unit]
Description=Wiz-War game server
After=network.target
[Service]
Type=simple
User=wizwar
WorkingDirectory=/opt/wizwar/packages/server
Environment=PORT=8787
Environment=WIZWAR_DATA_DIR=/var/lib/wizwar/rooms
Environment=WIZWAR_STATIC_DIR=/opt/wizwar/packages/web/dist
ExecStart=/usr/bin/npx tsx src/index.ts
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target