Deployment Pipeline
Image-based promotion through three environments on Fly.io. The same immutable Docker image moves from development to staging to production. See CI-CD Workflows for all workflow details.
Environments
| Environment | Fly App | Trigger | Database |
|---|---|---|---|
| Development | renewa-app | Push to main | renewa-db (Fly.io managed) |
| Staging | renewa-app-staging | Manual promotion | renewa-db-staging |
| Production | renewa-app-production | Manual promotion | renewa-db-production |
| PR Preview | renewa-app-pr-{N} | PR opened/updated | Ephemeral per-PR database |
All clusters are in the Frankfurt (fra) region.
Deployment Flow
Push to main
│
▼
main-deploy.yml
├── Build Docker image
├── Push to Fly.io registry
├── Sync secrets (GitHub → Fly.io)
└── Deploy to development
│
▼ (manual trigger)
promote-image.yml
├── Pull image from source environment
├── Tag for target environment
└── Deploy to staging
│
▼ (manual trigger)
promote-image.yml
└── Deploy to production
Promote command:
gh workflow run promote-image.yml \
-f source_environment=development \
-f target_environment=stagingKey Workflows
| Workflow | File | Purpose |
|---|---|---|
| Deploy to dev | main-deploy.yml | Build + deploy on push to main |
| Promote image | promote-image.yml | Move image between environments |
| PR preview | pr-preview.yml | Ephemeral app per PR |
| PR cleanup | pr-cleanup.yml | Destroy preview on PR close |
| Security scan | security-scan.yml | Trivy, Semgrep, Gitleaks, npm audit |
| Quality checks | quality-checks.yml | Lint, typecheck, tests |
| E2E tests | e2e-tests.yml | Playwright end-to-end tests |
| Migration tests | migration-tests.yml | Validate Atlas migrations |
See CI-CD Workflows for the full list of 20 workflows.
PR Preview Deployments
Each PR gets an isolated Fly.io app with its own database. See PR Preview Deployments for details.
- App name:
renewa-app-pr-{number} - Database: Created on first deploy, recreated only when schema/migration files change
- Data: Persists across non-schema commits within the same PR
- Cleanup: Automatic on PR close via
pr-cleanup.yml
The ci-gate job in pr-preview.yml is the single required status check for merging. It validates that all upstream jobs passed or were skipped.
Fly.io Configuration
| File | Environment |
|---|---|
fly.development.toml | Development |
fly.staging.toml | Staging |
fly.production.toml | Production |
fly.pr-preview.toml | PR previews |
Docker Build
Multi-stage Dockerfile at renewa-one/Dockerfile:
- deps — Install Bun dependencies
- frontend-build — Vite production build
- backend-build — Bundle backend
- production — nginx + supervisord running nginx and backend
See Docker Setup for local development compose stack.
Secret Management
GitHub Environment Secrets are synced to Fly.io on every deploy using --stage flag.
Required secrets: DATABASE_URL, DATABASE_URL_MIGRATION, JWT_SECRET, FLY_API_TOKEN, ENCRYPTION_MASTER_KEY, TIGRIS_*, UPSTASH_REDIS_*, SENTRY_DSN_BACKEND, BIRD_API_KEY, HUBSPOT_CLIENT_SECRET.
Migration Strategy
| Context | How Migrations Run |
|---|---|
| Local / Test | On container startup |
| Cloud (dev/staging/prod) | Via Fly.io release_command using DATABASE_URL_MIGRATION |
See Database Migrations for the Atlas migration workflow.
Related Pages
- Architecture Overview — System architecture context
- CI-CD Workflows — All 20 GitHub Actions workflows
- Docker Setup — Local Docker Compose configuration
- PR Preview Deployments — Ephemeral preview environment details
- Security Scanning — Trivy, Semgrep, Gitleaks, npm audit
- Database Migrations — Atlas migration system
- Database Architecture — PostgreSQL topology
- Git Workflow — Branch rules and PR process