6.5 KiB
CI/CD Target Architecture
Goal
This document captures the intended delivery model for CapaKraken without replacing the currently working manual production setup immediately.
The target state is:
- CI validates every PR.
- GitHub Actions builds immutable Docker images.
- Staging and production pull those exact images from a registry.
- Database migrations run as an explicit deploy step.
- Traffic is considered safe only after the app answers
GET /api/ready.
Core Idea
The production host should stop building application code from a Git checkout. Instead, it should only:
- pull a versioned
appimage - pull a matching
migratorimage - run Prisma deploy migrations
- start the application container
- wait for readiness
That removes "works on the server but not in CI" drift and makes rollbacks much simpler.
Delivery Flow
1. Pull Request Validation
The existing CI workflow continues to validate:
- typecheck
- lint
- unit tests
- build
- E2E
This remains the quality gate before merge.
2. Image Build
The new manual workflow release-image.yml builds two images from Dockerfile.prod:
runnertarget as the production app imagemigratortarget as the Prisma migration image
Recommended tag format:
sha-<git-commit>
Example:
ghcr.io/<owner>/capakraken-app:sha-abc123
ghcr.io/<owner>/capakraken-migrator:sha-abc123
3. Staging Deploy
The staging workflow deploy-staging.yml is intended to:
- connect to the staging host over SSH
- copy the deploy assets
- export
APP_IMAGEandMIGRATOR_IMAGE - run deploy-compose.sh
The compose file used for this target flow is docker-compose.cicd.yml.
4. Production Promotion
The production workflow deploy-prod.yml follows the same logic as staging, but the image tag is promoted manually.
That means production uses an image that was already built and can already have been exercised in staging.
Required Infrastructure
Minimum
- GitHub repository with Actions enabled
- GHCR or another container registry
- 1 Linux host with Docker and Docker Compose
- PostgreSQL
- Redis
- reverse proxy such as nginx
- SSH access from GitHub Actions to the host
Recommended
- separate staging and production hosts
- GitHub Environments for
stagingandproduction - required reviewer approval for
production - backup strategy for PostgreSQL volumes
- uptime monitoring and error tracking
Secrets
GitHub Environment Secrets
For staging:
STAGING_SSH_HOSTSTAGING_SSH_PORTSTAGING_SSH_USERSTAGING_SSH_KEYSTAGING_DEPLOY_PATHSTAGING_APP_HOST_PORTSTAGING_GHCR_USERNAMESTAGING_GHCR_TOKEN
For production:
PROD_SSH_HOSTPROD_SSH_PORTPROD_SSH_USERPROD_SSH_KEYPROD_DEPLOY_PATHPROD_APP_HOST_PORTPROD_GHCR_USERNAMEPROD_GHCR_TOKEN
Host-side Files
Each target host should already have:
.env.production- Docker installed
- network access to the container registry
The repository now also contains a small host example at tooling/deploy/.env.production.example and an operator note at tooling/deploy/README.md.
Minimum Host Bootstrap
For each target host, create a dedicated deploy directory such as /opt/capakraken and place these files there:
docker-compose.cicd.yml
.env.production
tooling/deploy/deploy-compose.sh
.env.production should hold the long-lived runtime settings, including:
POSTGRES_PASSWORD=<long-random-password>
NEXTAUTH_URL=https://capakraken.example.com
NEXTAUTH_SECRET=<long-random-secret>
GitHub Actions only injects the short-lived image references through deploy.env. The deploy script then loads both files before calling Docker Compose, so compose interpolation and container runtime env use the same source of truth.
Database Policy
For release environments, use:
pnpm --filter @capakraken/db db:migrate:deploy
Do not use db:push as the main production deployment mechanism. db:push is convenient for local development, but it does not give the release traceability that a migration-based deploy requires.
Rollback Model
Rollback should be image-based:
- choose the previous good
sha-...tag - run the production deploy workflow again with that tag
- confirm readiness
This is only safe when schema changes follow backwards-compatible expand and contract rules.
How A Production Update Works
The intended production update path is:
- merge to
mainafter the existing CI workflow is green - run release-image.yml to build immutable
appandmigratorimages tagged assha-<commit> - run deploy-staging.yml with that exact image tag
- GitHub Actions uploads the deploy bundle to the staging host and writes a temporary
deploy.env - deploy-compose.sh pulls images, starts PostgreSQL and Redis, runs Prisma deploy migrations, starts the new app container, and waits for
GET /api/ready - after staging is accepted, run deploy-prod.yml with the same tag
- production repeats the same image-based flow, so the running artifact matches staging
That means the production host no longer builds from Git. It only receives a versioned image and starts it after migrations complete.
Current Status
The repository now contains the CI/CD scaffolding, but the existing manual production setup remains untouched:
- current manual compose flow: docker-compose.prod.yml
- current manual runbook: ci-cd-manual.md
This allows the team to introduce the new path gradually instead of switching production in one step.