Production images
Build from the repository root (no source mounts or runtime package installs):
docker build --target frontend -t curltastic-frontend .
docker build --target backend -t curltastic-backend .
| Target | Listener | Runtime user | Healthcheck |
|---|---|---|---|
frontend (default final target) |
HTTP 8080 |
101:101 |
GET /healthz |
backend |
HTTP/WebSocket 3000 |
10001:10001 |
GET / |
The frontend build argument VITE_BASE_PATH defaults to /curltastic/ and is
passed to vite build --base. There are no required runtime environment
variables, credentials, writable application directories, or persistent volumes.
The Rust server keeps rooms in memory; rebuilding/restarting it loses games.
Compose integration contract
- Publish/route only frontend port 8080. Explicitly set Traefik's load-balancer port to 8080; do not infer it from the upstream nginx image metadata.
- Traefik routes
/curltastic/and strips/curltasticbefore forwarding. - Attach frontend to Traefik's external
webnetwork and a private application network. Put backend only on that private network, with the service/DNS namecurltastic-backend. Do not publish backend port 3000 or enable Traefik on it. - The browser requests public
/curltastic/wsusing its existing host, port and TLS scheme. nginx receives/wsand proxies the upgrade and query string tocurltastic-backend:3000/ws. It also proxiesPOST /room, exposed publicly as/curltastic/room. The current frontend creates room IDs locally and does not make HTTP room requests; there is no separate frontend HTTP API URL to set. - Docker DNS is re-resolved every five seconds so backend replacement during Compose watch does not leave nginx using a stale container address.
- Recommended runtime settings:
read_only: true,cap_drop: [ALL],security_opt: [no-new-privileges:true]. Give frontend a writable/tmptmpfs (mode 1777) for nginx PID/temp files. Backend needs no writable mount. - Never mount source code, a Docker socket, or host credentials. The build context
is allowlisted by
.dockerignore; host dependencies and.env*are excluded. - Use Compose watch
action: rebuild: frontend changes rebuild thefrontendtarget, backend changes rebuild thebackendtarget. Ignore node_modules, dist, target, .git and other generated files. Watch Dockerfile for both services anddocker/nginx.conffor frontend.
The nginx listener expects already-stripped paths; a raw host-port request to
/curltastic/ does not simulate Traefik. Test the public route through a
prefix-stripping test proxy, or request /, /assets/..., /ws, /room directly
when testing nginx alone.
Development and verification
Local npm run dev still defaults to / and connects to the page hostname on
backend port 3000. Existing VITE_WS_URL overrides are preserved for custom local
setups; Vite variables are build-time settings, not runtime container settings.
Room navigation and share links retain the page path.
(cd frontend && npm ci && npm test -- --pool=forks --poolOptions.forks.singleFork)
(cd frontend && npm run build -- --base=/curltastic/)
(cd backend && cargo test --locked)
The frontend Docker build runs its tests and TypeScript checking before producing static assets. The backend build uses the committed Cargo lockfile.
Verification at implementation: frontend 26 tests passed, Rust 22 tests passed, and the production subpath frontend build passed. Both Docker build attempts were denied by the execution approval layer; image builds, nginx syntax, and live HTTP/WebSocket proxy behavior therefore remain unverified. No servers were started and no deployment was performed.