Skip to main content

Deploy to Production

To run a public server, the engine repo ships a deploy.sh that installs a prebuilt release as a hardened systemd service on a Linux host. This is how the official servers are run.

What the script does

dev/deploy.sh (in the RabiRiichi repo):

  • Installs the .NET 9 ASP.NET runtime automatically if it's missing.
  • Downloads a release asset (RabiRiichi.Server-<tag>.zip) from GitHub Releases and unpacks it to the install dir.
  • Creates a dedicated OS user, writes an environment file, and installs a hardened systemd unit (auto-restart, NoNewPrivileges, ProtectSystem, etc.).
  • Generates a random JWT_SECRET on first install and retains it on subsequent upgrades.

Requirements on the target host: bash, curl, unzip, systemctl (systemd), and root / sudo.

Deploy

# on the target Linux server, from a RabiRiichi checkout (or just the script)
sudo ./dev/deploy.sh

That installs the latest release with defaults:

SettingDefault
Service userriichi
Install dir/opt/rabiriichi
Bind addresshttp://0.0.0.0:5000
Env file/etc/rabiriichi/environment
Service namerabiriichi (systemd)

Common options

sudo ./dev/deploy.sh \
--port "http://0.0.0.0:5000" \
--tag "v0.1.3" \
--dir "/opt/rabiriichi" \
--user "riichi" \
--jwt-key "$(openssl rand -base64 48)" \
--env-file ./my.env
OptionMeaning
-p, --portASPNETCORE_URLS value (bind address/port).
-t, --tagRelease tag to deploy (default latest).
-d, --dirInstall directory.
-u, --userOS user to run the service.
-k, --jwt-keyJWT secret (auto-generated & retained if omitted).
-e, --env-fileExtra key=value env file merged into the service environment.
--undeployStop and completely remove the service, user, and files.

Use --env-file to enable replays or override any appsettings.json value, e.g.:

# my.env
RABIRIICHI_GAME_SAVE_DIR=/var/lib/rabiriichi/saves
RABIRIICHI_GAME_SAVE_TTL=604800
Logging__LogLevel__Default=Warning

Manage the service

systemctl status rabiriichi
journalctl -u rabiriichi -f # follow logs
systemctl restart rabiriichi
sudo ./dev/deploy.sh --tag v0.1.4 # upgrade (keeps the existing JWT_SECRET)
sudo ./dev/deploy.sh --undeploy # remove everything

The service listens on plain HTTP (e.g. :5000). For public play you'll want a reverse proxy that terminates TLS and upgrades WebSockets, so clients can reach it at wss://your-domain — required for any client served over HTTPS (see the mixed-content note).

Example nginx server block:

server {
listen 443 ssl;
server_name riichi-server.example.com;

ssl_certificate /etc/letsencrypt/live/riichi-server.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/riichi-server.example.com/privkey.pem;

location / {
proxy_pass http://127.0.0.1:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; # WebSocket upgrade
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 3600s; # long-lived game sockets
}
}

Clients then connect to wss://riichi-server.example.com.

Set a strong JWT secret

In production always use a strong, secret JWT_SECRET (the script generates one by default and retains it across upgrades). Anyone who knows it can forge tokens. See Authentication.

Deploy the web client too (optional)

The client is a static site, so host it anywhere. Build it and point users at your server:

# in RabiRiichi-Web
npm install && npm run proto:gen && npm run build # → dist/

Serve dist/ over HTTPS (Cloudflare Pages, Netlify, nginx, …). To make your server the default choice, edit src/config/servers.ts before building — see Host the web client.

Checklist

  • .NET 9 runtime present (the script installs it if not).
  • deploy.sh run; systemctl status rabiriichi is active.
  • Strong JWT_SECRET set.
  • TLS reverse proxy in front, reachable at wss://….
  • (Optional) replays enabled via RABIRIICHI_GAME_SAVE_DIR.
  • (Optional) web client built and hosted, pointed at your server.