Deployment
Guidely is a standard Next.js application plus a PostgreSQL database and an optional worker.
Container
docker build -f apps/web/Dockerfile -t guidely-web .
docker run -p 3000:3000 \
-e DATABASE_URL=postgresql://user:pass@host:5432/guidely?schema=public \
-e SESSION_SECRET=$(openssl rand -base64 48) \
-e APP_URL=https://guidely.example.com \
-e NEXT_PUBLIC_GUIDELY_API_URL=https://guidely.example.com \
guidely-web
The image's entrypoint runs prisma migrate deploy and then the server; run the worker from the same image with the worker command and seed demo data with seed. DATABASE_URL can also be composed from DB_HOST/DB_PORT/DB_NAME/DB_USER/DB_PASSWORD.
docker compose --profile full up runs Postgres + web locally from the compose file.
For AWS (ECS Fargate + RDS + public ALB + GitHub organization login) use the CDK app in infra/, documented in aws-deployment.md.
Run migrations before each release: pnpm db:deploy (or npx prisma migrate deploy with the schema in the image at apps/web/prisma).
Run the worker as a second process/container with the same image and node_modules: pnpm worker (it is a plain Node process using pg-boss; one replica is enough, more are safe).
Platforms
- Vercel / Netlify (dashboard + API): set env vars, build command
pnpm build:packages && pnpm --filter @guidelytech/web build, output directoryapps/web/.next. Run the worker elsewhere (Railway/Fly/ECS cron) since serverless has no long-lived process. - Fly.io / Railway / Render / ECS / Kubernetes: use the Dockerfile; mount nothing; add a Postgres and (optionally) Redis.
- Put the SDK bundle (
apps/web/public/sdk/guidely.js) on a CDN and setNEXT_PUBLIC_GUIDELY_CDN_URLfor faster customer loads.
Operational checklist
- Health:
GET /api/healthreturns{ status, db: { ok, latencyMs }, metrics }(503 when the DB is down). - Logs: JSON lines on stdout; set
LOG_LEVEL. - Errors: wire
captureExceptioninapps/web/src/server/logger.tsto Sentry/Datadog. - Metrics:
incr()counters are exposed on the health endpoint; forward them to StatsD/Prometheus from there. - Secrets: never in the image; inject at runtime.
- CORS: configure allowed origins per environment in Settings → Environments; leave empty only for development.
- Backups: use managed Postgres PITR or
pg_dump --format=custom guidely > backup.dumpdaily; restore withpg_restore --clean --if-exists -d guidely backup.dump. ImmutableGuideVersionrows make restoring a single guide possible by re-pointingGuideEnvironmentState. - Rollback a release: deploy the previous image; migrations are additive (never drop columns in the same release that stops writing them).
- Scaling: the web tier is stateless (sessions in Postgres, caches in Redis when configured). Event ingestion writes in batches with
createMany … skipDuplicates; at roughly >50M events/month moveEventIngestServicebehind a queue and reporting onto rollups/columnar storage (see architecture doc).