Skip to content

Deployment

Hails edited this page Jul 4, 2026 · 3 revisions

Deployment

This page covers putting hailsDotGO on your own Linux server: one-time provisioning with deploy/setup.sh, the Caddy reverse proxy, the systemd unit, and the two PowerShell deploy scripts that build on your Windows machine and push over SSH. The reference setup is a small Debian-family VPS with Caddy terminating HTTPS in front of the app on port 8080, and MySQL either local or on a separate host (see Database-Guide).

Overview

Internet ── 80/443 ──> Caddy ── localhost:8080 ──> hailsDotGO (systemd, user hailsdotgo)
                                                      │
                                                      └──> MySQL (localhost:3306 or remote)

The app runs as an unprivileged system user from /opt/hailsdotgo, reads its secrets from /opt/hailsdotgo/app.env, and is managed by systemd. Deploys happen from a workstation: build TypeScript and a Linux binary locally, upload over SSH, restart the service.

1. One-time server provisioning: deploy/setup.sh

Copy the deploy/ directory (plus hailsdotgo.service from the repo root) to the server and run the script once as root from the directory containing those files:

sudo ./setup.sh

It performs four steps:

  1. Installs Caddy from the official Caddy apt repository.
  2. Creates the app user and directories: a system user hailsdotgo with no login shell, and /opt/hailsdotgo with static/js, static/css, and templates subdirectories, owned by that user.
  3. Installs the systemd unit: copies hailsdotgo.service to /etc/systemd/system/, reloads the daemon, and enables the service (it is not started yet, since nothing has been deployed).
  4. Installs the Caddyfile to /etc/caddy/Caddyfile (Caddy is not restarted yet).

After your first deploy, finish with systemctl restart caddy and systemctl start hailsdotgo.

SSH key authentication is required

The main deploy script connects with BatchMode=yes, which forbids password prompts. Create a key pair, install the public key in the server's authorized_keys, and keep the private key at the path the script expects (~/.ssh/your_deploy_key; the script in the repo looks for %USERPROFILE%\.ssh\hailsdotgo, so either use that name or adjust the script).

2. The Caddyfile

Caddy handles HTTPS certificates automatically. The relevant block is just:

pogo.example.com {
    encode gzip zstd
    reverse_proxy localhost:8080
}

Replace the hostname with your own domain (with DNS pointing at the server) before restarting Caddy.

Note for your own install: the example deploy/Caddyfile in the repo contains a second site block that reverse-proxies an unrelated radio service (AzuraCast) running on the author's server. Delete that block. Likewise, deploy/docker-compose.override.yml exists only to move that radio service's web ports off 80/443 so Caddy can own them; unless you also run AzuraCast on the same box, ignore that file entirely.

3. The systemd unit

hailsdotgo.service (repo root) is short enough to read in full:

[Unit]
Description=hailsDotGO Pokemon GO tool
After=network.target

[Service]
Type=simple
User=hailsdotgo
Group=hailsdotgo
WorkingDirectory=/opt/hailsdotgo
ExecStart=/opt/hailsdotgo/hailsDotGO
Restart=always
RestartSec=5
EnvironmentFile=/opt/hailsdotgo/app.env

[Install]
WantedBy=multi-user.target

Key points:

  • Restart=always with RestartSec=5: the app comes back automatically after a crash or an OOM kill.
  • EnvironmentFile=/opt/hailsdotgo/app.env: all configuration (database credentials, CSRF key, PayPal, transactional email, GitHub sync) is loaded from this file, which the deploy script generates and uploads with mode 600. See Configuration.
  • WorkingDirectory=/opt/hailsdotgo: relative defaults like cache/ and locales/ resolve under the app directory.

The main deploy script also tracks this unit file: if it changes, the script copies the new version into /etc/systemd/system/ and runs daemon-reload for you.

4. Deploying with the main deploy.ps1 (repo root)

Run from the repo root on a Windows machine with Go, Node, and Windows OpenSSH installed:

.\deploy.ps1

It needs VPS_HOST, VPS_USER, DB_HOST, and SUPERADMIN_USER in your .env (it throws immediately if any is missing) and the SSH key described above. The full cycle:

  1. Build: npm run build for the TypeScript bundles, then a linux/amd64 cross-compile with CGO_ENABLED=0 and -ldflags "-s -w".
  2. Generate app.env from your .env values (database, superadmin, CSRF, PayPal, transactional email, GitHub, push notifications), written as UTF-8 without BOM.
  3. Diff against the manifest: every tracked file is hashed with SHA256 and compared to deploy-manifest.json from the last successful deploy, so only changed files upload. Tracked files include the binary, static/css/main.css, static/maintenance.html, every template, the systemd unit, every *.js bundle found in static/js/ (discovered dynamically), and the bundled sprite directory. If nothing changed, the script just refreshes app.env and exits without touching the service.
  4. Stop the service (systemctl stop hailsdotgo), only when there is something to upload.
  5. Upload: regular files go over scp with up to 3 attempts across 3 rounds with randomized waits; the large binary is streamed over SSH stdin (cat > dst) with compression and keepalives, retried up to 5 rounds, because plain scp proved unreliable for it; directories are tarred and piped through SSH.
  6. Fix up: chmod +x on the binary, app.env to mode 600, chown -R hailsdotgo:hailsdotgo /opt/hailsdotgo, and a systemd daemon-reload if the unit file changed.
  7. Start and verify: systemctl start hailsdotgo, wait, check systemctl is-active, and tail the journal. The manifest is saved only on success, so a failed deploy retries everything changed next run.

5. The simpler deploy/deploy.ps1 alternative

If you prefer something with no manifest and no .env parsing:

.\deploy\deploy.ps1 -VpsUser youruser -VpsHost your.server.com
.\deploy\deploy.ps1 -VpsUser youruser -VpsHost your.server.com -SshPort 2222

It builds the same way, then always uploads the binary plus the entire templates/ and static/ trees with scp -r, fixes permissions, and restarts the service. Slower and less clever, but with fewer moving parts. Note it does not generate or upload app.env; manage that file yourself when using this script.

OCR microservice

The IV Calculator screenshot scanner calls a self-hosted neural OCR microservice (ocr-service/, RapidOCR). Tesseract is no longer used.

  • It is a small Python service that listens on 127.0.0.1:18265 and is local-only (never exposed publicly). The Go app reaches it via OCR_SERVICE_URL (default http://127.0.0.1:18265/ocr).
  • ocr-service/ ships its own setup.sh, requirements.txt, and a hailsdotgo-ocr.service systemd unit. Install it once on the server (creates a venv, installs requirements, enables the unit); see ocr-service/README.md.
  • deploy.ps1 tracks the ocr-service directory: it syncs the file contents to /opt/hailsdotgo-ocr and restarts the OCR service on deploy. A change to requirements.txt still needs a manual pip install in the service's venv on the server.
  • The service is optional. If it is down the Screenshot tab still loads and accepts uploads; it just returns empty extraction fields.

The main deploy script also discovers templates/*.html dynamically (the same way it globs the JS bundles), so newly added page templates are always uploaded.

Server directory layout

Path Contents
/opt/hailsdotgo/hailsDotGO The Go binary
/opt/hailsdotgo/app.env Environment file, mode 600
/opt/hailsdotgo/templates/ HTML templates, read at startup
/opt/hailsdotgo/static/ CSS, compiled JS bundles, sprites, maintenance.html
/opt/hailsdotgo/cache/ Runtime game data cache (created by the app, disposable)
/opt/hailsdotgo/locales/ Translation overrides and backup/ (created by the app, back this up)
/opt/hailsdotgo/hailsdotgo.service Uploaded copy of the unit; the live one is in /etc/systemd/system/
/etc/caddy/Caddyfile Reverse proxy config
/opt/hailsdotgo-ocr/ OCR microservice (synced by deploy.ps1; runs under its own systemd unit)

Ports

Port Who Exposure
80, 443 Caddy Public (HTTP redirect + HTTPS)
8080 hailsDotGO Should be reachable only by Caddy. The app binds :8080 on all interfaces, so use a firewall to block external access to it
3306 MySQL Local only, or firewalled to the app host if the database lives elsewhere
18265 OCR microservice Bound to 127.0.0.1 only; reached by the app over localhost, never exposed

Once deployed, see Operations for logs, maintenance toggles, and backups.

Clone this wiki locally