Optional independently deployed marketing surface
The control-plane deployment remains the authoritative SaaS application. A single, reviewed BoardReadyOps web runtime image can also run as a small separate, public-only marketing container. This does not create a second frontend codebase or require a new hosted vendor.
This is an opt-in deployment mode. The current production hostname and ingress are unchanged until an operator explicitly commissions a separate marketing origin. The deployment can start, stop, be upgraded or rolled back without restarting the SaaS web, worker, PostgreSQL or Redis services.
Runtime boundary
Set BOARDREADYOPS_SITE_SURFACE=marketing in the marketing container only.
This mode exposes the marketing homepage and public discovery files, the
immutable /_next/static/ assets, public demo files, and a basic liveness
route. It returns HTTP 404 for other paths, including OAuth, webhooks,
/api/v1/*, hosted runs, dashboard, billing and repository APIs. Non-read
requests are denied. Do not give this container a production .env, database
connection, GitHub private key, OAuth secret, artifact volume or worker.
Existing SaaS remains in the default app mode. Its sessions, API and OAuth
behavior are unchanged. The marketing homepage intentionally stays signed
out; customer actions such as /setup link to the configured SaaS origin
(default https://boardreadyops.com). The optional marketing origin can be
a different hostname without needing to duplicate authentication state.
Independent release and verification
Build and validate the existing apps/web/Dockerfile runtime image via the
normal protected repo/CI process, then use a reviewed, locally available
immutable source-SHA image tag:
export BOARDREADYOPS_MARKETING_IMAGE_TAG=<reviewed-commit-sha>
export BOARDREADYOPS_MARKETING_APP_ORIGIN=https://boardreadyops.com
docker compose -f deploy/docker-compose.marketing.yml config --quiet
docker compose -f deploy/docker-compose.marketing.yml up -d marketing
This Compose project is separate from the SaaS Compose project and publishes
only to 127.0.0.1:3002 by default. There is no new production ingress,
DNS record or additional always-running service until specifically configured.
To expose a public marketing domain, the operator must separately set up HTTPS
ingress to that local listener; retain the original SaaS origin for OAuth and
all customer routes. Deploy, inspect and roll back the marketing container
independently by pinning a different known-good BOARDREADYOPS_MARKETING_IMAGE_TAG;
do not couple it to the control-plane BOARDREADYOPS_IMAGE_TAG.
Expected smoke behavior on the marketing listener: GET / and
GET /api/health/live return 200; GET /setup, GET /api/v1/runs,
POST /api/v1/runs, GET /api/auth/github/login, and
GET /openapi.json return 404. The public homepage links to the actual
BoardReadyOps GitHub App PASS/BLOCK pull requests and to the SaaS setup page.
Marketing-only route filtering is not a replacement for SaaS authorization.
This option establishes independent deployability but not proof of a separately commissioned production hostname, a final DNS cutover, an actual customer manufacturing release, or a dedicated independently built static marketing artifact. Record such acceptance only after verifying it in the chosen deployed environment.