Deployment

Deploy the frontend to Cloudflare Workers, run the full stack in Docker, and wire up the release pipeline.

Deployment

ShockStack has two deploy shapes:

  1. Frontend on Cloudflare Workers — the Astro app runs at the edge with the @astrojs/cloudflare adapter.
  2. Full stack in Docker.NET API + Postgres + frontend, orchestrated by Compose (or any container platform).

Pick the one that matches your shape. They share CI.

Environment variables

VariableWherePurpose
DATABASE_URLFrontendDrizzle / Better Auth database connection (Postgres).
BETTER_AUTH_SECRETFrontendHMAC secret for sessions. Rotate per environment.
BETTER_AUTH_URLFrontendPublic origin — e.g. https://app.example.com. Must match the live URL.
CLOUDFLARE_API_TOKENCIToken with Workers: edit scope.
CLOUDFLARE_ACCOUNT_IDCITarget account.
ConnectionStrings__DefaultConnectionBackendEF Core connection string.
Cors__Origins__0BackendAllowed frontend origin.

Frontend: Cloudflare Workers

frontend/astro.config.* uses the Cloudflare adapter; frontend/wrangler.jsonc declares bindings (R2 bucket, compatibility flags).

Deploy the frontend

bash

$ pnpm tokens:build

$ pnpm --filter frontend build

$ pnpm --filter frontend wrangler deploy

CI authenticates with CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID. For secrets like BETTER_AUTH_SECRET, use:

pnpm --filter frontend wrangler secret put BETTER_AUTH_SECRET

R2 for file uploads

wrangler.jsonc ships with an R2 bucket binding. If you don’t need file storage, remove the binding; if you do, create the bucket once:

pnpm --filter frontend wrangler r2 bucket create shockstack-uploads

The server-side upload route reaches it via context.locals.runtime.env.UPLOADS (typed through env.d.ts).

Full stack: Docker

docker/docker-compose.yml brings up Postgres + the API + the frontend. Good for staging, self-hosted prod, or anywhere you don’t want edge.

Docker deploy

bash

$ docker compose -f docker/docker-compose.yml build

$ docker compose -f docker/docker-compose.yml up -d

$ curl -f http://localhost:8080/health

Push the built images to your registry (GHCR, ECR, etc.) and run them through your usual orchestrator — Kubernetes, Fly, Railway, a single VM. Nothing in the stack is Docker-specific beyond the Dockerfile.

CI/CD

.github/workflows/ uses paths-filter to run only what changed:

  • frontend paths → lint → typecheck → test → build.
  • backend paths → dotnet restoredotnet testdotnet build.
  • tokens paths → pnpm --filter @shockstack/tokens build.

semantic-release runs on main, reads Conventional Commits, bumps the version, generates CHANGELOG.md, and tags the release. Deploys are triggered from tags.

Rollout checklist

Use this before every cutover.

  • Secrets set (BETTER_AUTH_SECRET, DATABASE_URL, Cloudflare tokens).
  • BETTER_AUTH_URL matches the live origin.
  • Database migrations applied (pnpm ss db migrate locally against the target, or via CI).
  • Health endpoints respond (/health on the backend, /api/health on the frontend if enabled).
  • Sign-in & session roundtrip works from the production URL.
  • Observability hooks in place (logs, traces, error reporting).
  • Rollback plan: previous image tag on hand; wrangler rollback for edge.

Rolling back

Cloudflare pnpm —filter frontend wrangler rollback [—version-id]
Docker docker compose up -d frontend —image ghcr.io/you/shockstack-frontend:PREV

Both are quick; preferred over trying to patch forward in an outage.