Deploy SteamOS.Club as a hardened OCI container
This adapter packages the reviewed xyd static output in an unprivileged NGINX runtime. It is suitable for a local preview, an OCI registry handoff, or a private origin behind a selected ingress. It does not change DNS, obtain certificates, publish an image, or expose a public service.
Do not deploy an image built from an uncommitted or unreviewed working tree. Record the source revision, static artifact digest, image ID or registry digest, approver, and rollback image before changing production traffic.
Deployment boundary
| Layer | Responsibility |
|---|---|
xyd build | content, routes, native cosmo UI, search, SEO files, canonical and locale links |
OCI image | exact static bytes, non-root NGINX, health endpoint, compression and cache policy |
Container runtime | read-only root, /tmp tmpfs, no capabilities, process and restart policy |
TLS ingress or CDN | certificates, HTTP redirect, HSTS, public rate limits and edge observability |
Release engineering | immutable identity, approval, smoke tests, backup, restore and rollback evidence |
HSTS is intentionally absent from the HTTP-only container. Sending it before a reviewed HTTPS ingress exists can create an incorrect security claim and complicate recovery.
Artifact contract
Run the complete source and production gates first. The Docker build context admits only deploy/ and .xyd/build/client/; it excludes source files, dependency trees, Git data, generated manifests under .vite, and local secrets.
The runtime base is pinned by a multi-platform digest. org.opencontainers.image.revision records the reviewed source identifier, while the image ID or registry digest identifies the packaged bytes. A tag is only a movable human label.
Local build and smoke test
The project test discovers either direct Docker access or passwordless sudo docker. It builds the image, starts it on a random loopback port, exercises both locales, validates headers and unknown-route behavior, checks the runtime restrictions, saves and reloads the image, then starts the restored copy.
The test uses a unique temporary tag and container names. Its cleanup trap removes only those Phase 9 resources; it does not prune shared images, volumes, networks, or build caches.
Production runtime contract
The checked-in Compose adapter binds to 127.0.0.1 by default. It runs as UID/GID 101, drops every Linux capability, enables no-new-privileges, caps PIDs, makes the root filesystem read-only, and provides only a 16 MiB /tmp tmpfs required by unprivileged NGINX.
Do not change the bind address to 0.0.0.0 until firewall, TLS ingress, forwarded-header trust, monitoring, and incident ownership are reviewed.
Header and route verification
Check the private origin before attaching production traffic. HTML and route data require revalidation; fingerprinted /assets/ receive a one-year immutable policy. Dynamic gzip adds Vary: Accept-Encoding. Unknown paths return 404 rather than the SPA fallback with HTTP 200.
Required observations include HTTP 200 for both locale routes and health, HTTP 404 for the absent route, hidden NGINX version, nosniff, SAMEORIGIN, a restrictive referrer policy, a limited permissions policy, and the expected cache/compression headers.
Rollback
- Retain the reviewed static artifact and its SHA-256 digest from CI.
- Build once with the reviewed source revision; record the image ID and, after registry upload, its immutable digest.
- Deploy by digest or a release ID locked to that digest. Never rebuild an old tag with current dependencies.
- Keep at least one previously healthy image and its evidence during the retention window.
- On rollback, switch the ingress/runtime atomically, then repeat health, locale, discovery-file, and high-risk-page checks.
The Phase 9 workflow deliberately does not push to a registry. Registry authentication, signing, provenance attestations, retention, and vulnerability policy must be added for the selected platform rather than guessed here.
Backup and restore exercise
For an isolated local exercise, export the immutable image, record the archive digest, remove the test tag, load the archive, compare image IDs, and start the restored image. pnpm test:container automates this disposable path.
The source repository, lockfile, package patches, content snapshots, configuration, and workflows remain the primary backup. An exported runtime image is deployment/rollback evidence, not a substitute for source recovery.
TLS ingress checklist
- Serve only
https://steamos.club; redirect plain HTTP at the ingress. - Request and renew certificates through the selected provider's supported mechanism.
- Add HSTS only after HTTPS and rollback behavior are proven on the real domain.
- Preserve the original
Hostand client scheme; do not trust forwarded headers from arbitrary networks. - Retain request/error logs according to a documented privacy and deletion policy.
- Verify Brotli or gzip,
Vary, cache behavior, canonical URLs, both locales,sitemap.xml,robots.txt, andllms.txtfrom the public edge.
Frequently asked questions
Does this container terminate TLS for steamos.club?Does this container terminate TLS for steamos.club?
Why does the image copy an existing build instead of rebuilding xyd?Why does the image copy an existing build instead of rebuilding xyd?
Sources
- NGINX unprivileged container image
- Docker Compose services reference
- NGINX gzip module
- OCI pre-defined annotation keys
Version history
- 2026-07-15: Phase 9 provider-neutral OCI deployment, verification, backup, restore, and rollback guide published with a deployment-boundary architecture diagram.