This document describes the end-to-end deployment of catbeez-arcade, an authenticated WASM game server, onto a Linode instance. The deployment follows an incremental approach: provision infrastructure, configure DNS, set up a reverse proxy, and install the application — with secrets handled manually outside automation.
Key Concepts
Incremental deployment pattern — the deployment is broken into four discrete increments, each building on the previous one, following FTL2's incremental execution model.
Reverse proxy architecture — Caddy handles TLS termination on :443 and reverse proxies to the application on localhost:8000; the app never faces the internet directly.
Pre-baked images — uses a private Linode image (private/37121878, Fedora 43 with Caddy pre-installed) to skip base configuration, reducing deployment time and complexity.
DNS-only Cloudflare — the A record is not proxied through Cloudflare's CDN, meaning Caddy handles TLS via Let's Encrypt directly.
Manual secret injection — secrets (OAuth credentials, secret key, allowed emails) are entered interactively at startup rather than stored in automation or config files. This is a deliberate security boundary.
Nanode sizing — a 1 CPU / 1GB RAM instance is sufficient for serving static WASM files behind authentication.
Provision Linode: type g6-nanode-1, region us-east, image private/37121878
Wait for SSH readiness on port 22
Create Cloudflare A record pointing to Linode IP (DNS only, not proxied)
Write Caddyfile for reverse proxy, open firewall ports 80/443, enable caddy service
Upload wheel + game assets, install into uv venv at ~/.venv, stage games at ~/games
Relationships
Cloudflare DNS integration — same pattern used in ftl2-servercraft for DNS record management; Cloudflare module handles A record creation.
Caddy reverse proxy — recurring pattern across FTL2 deployments (expert-service, servercraft-web use similar Caddy configurations).
Incremental execution — demonstrates FTL2's increment model where each step has a clear precondition (e.g., Increment 3 requires the IP from Increment 1).
Vault integration boundary — secrets are handled manually here, but this is a candidate for Vault secret bindings in a more mature deployment.
uv package management — uses uv for Python venv creation, consistent with other FTL2-deployed Python services.
Exam-Relevant Points
The app binds to 127.0.0.1:8000 (localhost only) — it is not directly accessible from the internet; Caddy provides the public-facing endpoint.
Cloudflare DNS is set to DNS only (not proxied), so TLS is handled by Caddy/Let's Encrypt, not Cloudflare.
Firewall ports 80 and 443 must be opened explicitly — port 80 is needed for Let's Encrypt ACME challenges even though traffic is served on 443.
Secrets are a manual step — FTL2 automation intentionally does not handle them in this deployment, establishing a clear automation/manual boundary.
The Google OAuth callback URL must match exactly: https://arcade.catbeez18.com/auth/callback.
The deployment uses a private base image rather than provisioning from a stock OS — this is a performance optimization pattern that pre-bakes common dependencies.