This document describes the deployment architecture, Traefik reverse proxy overlay, and remote host automation used across dev, stg, and production environments.
[feature/xxx] ---> [dev] (tag vX.Y.Z -> CI builds & pushes to GHCR)
│
▼ (PR merge)
[stg] (CD deploys to Staging)
│
▼ (PR merge)
[main] (CD deploys to Production)
- Development (
dev):- Default working branch. Pushes directly to
devdo not deploy. - Run tests locally with
bash scripts/check.sh.
- Default working branch. Pushes directly to
- Release to GHCR (
v*):- Cut release:
bash scripts/release.sh vX.Y.Z. - Pushing the tag triggers
.github/workflows/ci.yml, running tests and pushing images (vX.Y.Z,vX.Y,latest) to GitHub Container Registry (ghcr.io).
- Cut release:
- Staging Promotion (
stg):- Open PR:
bash scripts/release.sh pr stg(orgh pr create --base stg --head dev). - Merging into
stgautomatically deploys to the Staging server.
- Open PR:
- Production Promotion (
main):- Open PR:
bash scripts/release.sh pr main(orgh pr create --base main --head stg). - Merging into
mainautomatically deploys to the Production server.
- Open PR:
Configure two environments in GitHub (Settings > Environments): staging and production. Both execute the exact same deployment job in .github/workflows/cd.yml.
| Variable / Secret | Type | Description |
|---|---|---|
NETBIRD_KEY or NETBIRD_SETUP_KEY |
Secret | NetBird mesh VPN setup key for runner access |
SSH_PRIVATE_KEY |
Secret | SSH private key to authenticate against deployment server |
SERVER_HOST |
Variable | Target server IP or NetBird mesh hostname |
SERVER_USER |
Variable | Target server SSH user |
SERVER_PROJECT_DIR |
Variable | Target directory on host (e.g. /opt/python-project-starter) |
NETBIRD_MANAGEMENT_URL |
Variable | (Optional) Self-hosted NetBird management URL |
In staging and production, applications run behind a shared Traefik gateway that manages Let's Encrypt SSL/TLS certificates and listens on ports 80/443.
In local .env:
COMPOSE_FILE=docker-compose.yml
FASTAPI_PORT=8000Application maps directly to http://localhost:8000.
In the server's .env:
COMPOSE_FILE=docker-compose.yml:docker-compose.traefik.yml
APP_HOSTNAME_DOMAIN=api-stg.ait.ac.thDocker Compose automatically layers docker-compose.traefik.yml, connects the container to the external traefik_public network, and registers routing and SSL with Traefik.
- Smoke Testing: Every deployment executes
scripts/smoke-test.shverifying/health, index route, and Django checks before declaring success. - Persistent Baseline (
.deploy_success): The deployed commit SHA is saved in.deploy_successonly after tests pass. - Zero-Touch Rollback: If deployment or smoke tests fail, the workflow immediately checks out the previous verified SHA from
.deploy_successand redeploys it, restoring the host to a stable baseline.