Production

Ship TaskLabs behind Dokploy + Cloudflare Tunnel with automated daily backups to S3-compatible storage.

The production stack

Production runs the same product shape as development: the Next.js frontend, the self-hosted Convex backend, Convex storage on Postgres, the dashboard, and a db-backup scheduler. The only intentional dev difference is hot reload — production has no watcher services, no source bind mounts, and no pnpm dev.

  • The frontend starts with next start on an explicit host and internal port.
  • A build-time NEXT_TURBOPACK_ROOT lets Next resolve the monorepo root and sibling Convex imports.
  • The dashboard stays internal — reach it over an SSH tunnel, never a public domain.
  • All five prod services are health-checked by Compose.

Origins & CORS

Public domains map to container ports: the frontend, the Convex API, and the Convex site proxy each get their own subdomain. FRONTEND_ORIGIN and CORS_ALLOWED_ORIGINS list these exact HTTPS origins, and CORS_ALLOWED_ORIGINS also accepts proxy-forwarded public hosts for Cloudflare/Dokploy.

Daily backups

The db-backup service runs a Postgres-only pg_dump --format custom at midnight EAT, uploads the dump to the configured RustFS bucket via the S3 API, and sends a Telegram confirmation with backup-specific bot credentials. Dev carries the same service behind a backup profile to keep stack parity without running scheduled dumps during hot reload.

  • RustFS endpoint, region, and access keys come from .env — never from source code.
  • The healthcheck confirms crond and the RustFS bucket before reporting healthy.

Convex Auth env

Self-hosted Convex Auth requires deployment env synced before functions deploy: SITE_URL, JWT_PRIVATE_KEY, and JWKS. The production deploy profile syncs these before pushing functions. Verify the OIDC and JWKS well-known endpoints after a change.

Security checklist

  • The prod admin key is generated on the live backend container, never hand-edited, and stored only in gitignored .env files.
  • POSTGRES_URL stays cluster-only; a /<database> path makes the backend exit.
  • No wildcard CORS and no local origin fallbacks.
  • Telegram origins are explicit env only.
  • Never commit .env, .env.dev, or generated secrets.