Deploying with the CI/CD Pipeline
Deploys are branch-driven and automatic: you merge to a branch, and GitHub Actions builds images, ships them, runs migrations, and brings the environment up. There's no manual deploy command in the normal flow.
The branch flow
- Merge to
staging→ the staging deploy runs: it builds and pushesstaging-latestimages and deploys to the staging environment. - Merge
staging→main→ the production deploy runs: it builds versioned images, deploys to production, and creates a GitHub Release (if the version is new).
CI checks run on the pull request before merge (via the ci-* workflows), not after — so the deploy workflow trusts the already-passed PR checks and doesn't re-run them.
What a deploy does
Each deploy workflow performs the same sequence on the server:
- read the version from the
VERSIONfile; - build and push the Docker images to GHCR;
- SCP the deploy files to
/app/{env}/; - start Postgres and wait for it to be healthy;
- run core migrations, then plugin migrations (see Migration Ordering at Deploy Time);
- start all services;
- start or restart the shared Caddy;
- verify API health.
What you set up once
For this to work, the server must be provisioned and four repository secrets configured — DEPLOY_SSH_KEY, DEPLOY_HOST, DEPLOY_USER, and GHCR_TOKEN (a classic PAT with write:packages). If your repo is under a GitHub org, you also need to allow Actions to publish packages (org → Settings → Actions → "Read and write permissions", and package creation enabled). A first-run 403 on docker push almost always traces back to those org package settings or a fine-grained (rather than classic) token.
Verifying a deploy
After a deploy, confirm health on the server with the environment's compose command — ... ps to see services and ... exec api wget -qO- http://localhost:4000/health to check the API (remembering the -p {env} project flag and the VERSION=... prefix; see Day-2 Operations).
The individual GitHub Actions workflows that implement all this are covered file-by-file in The CI/CD GitHub Workflows.