Skip to main content

Webv3 production container

Read this when you change Webv3's Dockerfile, runtime variables, startup or shutdown, or its image smoke check and publication.

frontend/apps/webv3/Dockerfile builds a standalone Nitro Node22 server for Linux amd64, including traced runtime dependencies. It runs as UID/GID 1000 on port 3000 and supports a read-only root filesystem. /healthz returns 200 without calling the API; use it for readiness and liveness rather than the homepage. TADOKU_API_URL is read at runtime and defaults in the image to https://tadoku.app/api/internal; a runtime environment override selects another Tadoku API. TADOKU_WEB_URL is also read at runtime and defaults in the image to https://tadoku.app. Development deployments override both variables. frontend/apps/webv3/server/plugins/runtime-env.ts checks these URLs, PORT, HOST, SERVER_SHUTDOWN_TIMEOUT and any NODE_EXTRA_CA_CERTS file before the server listens. A bad value makes the process exit with status 1 and a stderr line naming the variable, so a misconfigured pod never becomes ready. The line never prints the URL variables' values, which can carry credentials. On SIGTERM the server stops accepting connections and finishes in-flight requests for up to SERVER_SHUTDOWN_TIMEOUT seconds, 15 in the image, which is longer than the 8-second upstream deadline. Because it stops accepting at once, the Deployment needs a preStop delay so the edge removes the pod before it receives SIGTERM. The production config contract fails CI when these variables, the canonical hosts or /healthz are renamed or removed. Never pass credentials as build arguments. Supply any private CA certificate and NODE_EXTRA_CA_CERTS at runtime when the upstream requires it.

From frontend/:

docker build --platform linux/amd64 -f apps/webv3/Dockerfile -t webv3:check .
pnpm --filter webv3 exec playwright install chromium
PLAYWRIGHT_OUTPUT_DIR=/tmp/webv3-evidence node scripts/smoke-webv3-image.mjs webv3:check

The smoke command starts its own labeled container and synthetic GET-only API (frontend/scripts/smoke-webv3-fixture.mjs), including one contest with its logs and one standings learner's profile year and contest profile, uses nullable language lists by default (WEBV3_LANGUAGE_FIXTURE=array selects nonempty lists), checks server rendering, built assets, non-root/read-only operation and the existing browser journeys, then disconnects the API and checks health again. It also checks that a container with a bad TADOKU_API_URL, or with NODE_EXTRA_CA_CERTS naming a directory, exits non-zero naming the variable, and that docker stop lets a page waiting 6.5 seconds on the API finish. It also requests the guide, /pages/manual, concurrently with canonical and branch Host headers in production and development runtime modes, checking that account links stay with their matching origins, native links stay on the requested host and forwarded-host or branch headers cannot override the request Host. A second container verifies development runtime overrides against the same production-built image. It reaches the built createLog server function at the ID frontend/apps/webv3/server-function-id.mjs gives its file and variable; vite.config.ts hands that function to TanStack Start, so a server function's ID changes only when its file or variable is renamed, never with the bundler's output. A wrong ID answers 500 instead of the expected 403 and fails the smoke. An ID no build has must answer 409 with X-Tadoku-Outdated-Build and write no log line. It retains a browser report, container log and result with source revision and fixture boundary. This proves container behavior; it does not verify real API, authentication, DNS, ingress or deployment. Docker on Linux, Node22 and installed pnpm are required. The command removes only its own container.

.github/workflows/build-frontend-webv3.yaml runs checks and container journeys on pull requests. Main publishes immutable commit and latest tags to ghcr.io/tadoku/tadoku/frontend-webv3. The publication job rebuilds the image; its digest can differ from the smoke-checked image. The release gate then runs the credentialed suite against the published digest on a development tenant and, when it passes, tags that same digest prod without rebuilding. No pull request publishes an image.

The active production GitOps configuration makes next.tadoku.app follow the prod digest automatically through Image Updater and Argo CD. Webv3 uses the same production backend as Webv2; the homepage, leaderboards, contest lists and contest pages read contest data, content pages read the CMS and blog, native logging, contest registration and log moderation use the existing account, activity and contest API, and contest creation and scoring use its contest and scoring API. No product journey opens Webv2. Merging source that matches the Webv3 workflow's paths, including the shared lockfile, therefore republishes latest, and prod once the release gate passes, and rolls this production preview through Image Updater and Argo CD's automatic sync. A blocked release leaves production on its current prod digest. Require webv3 / container journeys on the exact reviewed source head before landing such changes. An authv2 production hold does not hold this Webv3 pipeline.

Before the first deployment, the package owner must confirm GHCR package visibility and cluster pull access. Newly created packages may be private; public unauthenticated pulls require making this package public, otherwise the cluster needs an authorized image pull credential.