# Deployment Runbook This document explains how deployments work for the EDR platform using Docker, GitHub Actions, and self-hosted runners. ## Overview - Monorepo contains 6 deployable services: - `freight-api` - `freight-portal` - `freight-backoffice` - `passenger-api` - `passenger-portal` - `passenger-backoffice` - Deployments run through one workflow: `.github/workflows/deploy.yml` - Each service is built/deployed independently in parallel (matrix jobs). - Docker Compose project names are branch-aware to avoid environment collisions on the same host. ## Prerequisites - Docker Engine with Compose plugin on the self-hosted runner. - GitHub self-hosted runner registered for this repository. - Repository secret configured: - `NPM_TOKEN` (for private `@tria-plc/*` package install during Docker build) - Server-side env files created for each branch/environment. ## Server Environment Files `sync-env-from-server.sh` reads env files from: `/home//environment/edr///` Where: - `` defaults to `tria` (overridable by `DEPLOY_USER`) - `` is derived from Git branch (lowercase, non-alphanumeric replaced with `-`) - `` is `edr-freight` or `edr-passenger` ### Required files per project For `edr-freight`: - `freight-api.env` - `freight-portal.env` - `freight-backoffice.env` - optional: `freight-web.build.env` For `edr-passenger`: - `passenger-api.env` - `passenger-portal.env` - `passenger-backoffice.env` - optional: `passenger-web.build.env` ### Required env key Each service env file must contain: - `PORT=` The sync script validates this and fails if missing. ### Build env files (optional) Used for build-time variables (example: API URLs for Vite/Next.js), with `export` syntax: ```bash export FREIGHT_VITE_API_URL=https://freight-api.example.com/api export PASSENGER_NEXT_PUBLIC_API_URL=https://passenger-api.example.com ``` These are injected into `GITHUB_ENV` during workflow execution. ## Docker Compose Port Mapping `docker-compose.yaml` uses per-service env variables for host/container port mappings: - `FREIGHT_API_PORT` - `PASSENGER_API_PORT` - `FREIGHT_PORTAL_PORT` - `FREIGHT_BACKOFFICE_PORT` - `PASSENGER_PORTAL_PORT` - `PASSENGER_BACKOFFICE_PORT` `scripts/deploy/sync-env-from-server.sh` extracts `PORT` from each synced `.env` and exports the corresponding `*_PORT` variable to `GITHUB_ENV`. ## Passenger Web Docker Configuration The passenger web apps (portal and backoffice) are deployed as **Next.js applications** using a dedicated Dockerfile: - Dockerfile: `infrastructure/docker/Dockerfile.passenger-web` - Apps: `apps/edr-passenger-web/portal` and `apps/edr-passenger-web/backoffice` ### Key differences from freight-web | Aspect | Freight Web | Passenger Web | | --- | --- | --- | | Framework | Vite (SPA) | Next.js (SSR/SSG) | | Deployment | Static export + nginx | Node.js server | | Dockerfile | `Dockerfile.web` | `Dockerfile.passenger-web` | | Final port (container) | 80 (nginx) | 5174/5184 (Next.js) | | Build arg | `TURBO_FILTER` | `APP_PACKAGE` + `APP_PATH` + `PORT` | ### Build arguments The Dockerfile accepts the following build args: - `APP_PACKAGE`: Turbo package filter (e.g., `@edr/passenger-portal`) - `APP_PATH`: App directory path (e.g., `apps/edr-passenger-web/portal`) - `PORT`: Container port to expose (e.g., `5174`) - `NEXT_PUBLIC_API_URL`: API URL visible to browser (e.g., `https://api.example.com`) ### Runtime The final image runs: ```bash node .next/standalone/server.js ``` This is the Node.js server provided by Next.js, configured to listen on the `PORT` env var. ## GitHub Actions Deployment Flow Workflow file: `.github/workflows/deploy.yml` ### 1) `prepare` job - Checks out repository once. - Creates workspace artifact (`workspace.tgz`) and uploads it. ### 2) `deploy` matrix job (parallel) For each service: - Downloads and extracts workspace artifact. - Syncs that service env file from server path. - Computes branch slug and sets: - `COMPOSE_PROJECT_NAME=-` - Creates `.npmrc`/`.npmrc_temp` from `NPM_TOKEN`. - Runs: - `docker compose --project-name "$COMPOSE_PROJECT_NAME" build ` - `docker compose --project-name "$COMPOSE_PROJECT_NAME" up -d ` - Cleans `.npmrc`/`.npmrc_temp`. ## Branch/Environment Isolation Compose project name is generated as: `-` Examples: - `edr-freight-main` - `edr-freight-staging` - `edr-passenger-dev` This prevents container/network/volume name collisions between branches. ## Local Manual Deployment (Optional) From repo root: ```bash DOCKER_BUILDKIT=1 docker compose build docker compose up -d ``` If private packages are required locally, create `.npmrc`: ```bash cat < .npmrc @tria-plc:registry=https://npm.pkg.github.com //npm.pkg.github.com/:_authToken= always-auth=true EOF ``` ## Passenger API Startup Behavior Passenger container entrypoint runs on startup: 1. `npm run prisma:generate` 2. `npm run prisma:migrate` (deploy mode) 3. `npm run prisma:seed` 4. starts API process ## Troubleshooting ### Missing env file Error: - `Missing env file: ...` Fix: - Create the required file in the server env directory for that project/branch slug. ### Missing PORT in env file Error: - `Missing required PORT in env file: ...` Fix: - Add `PORT=` to that service env file. ### Private package install fails Check: - `NPM_TOKEN` exists in repo secrets. - Workflow created `.npmrc` successfully. ### Prisma seed/migrate failures (passenger) Check: - `DATABASE_URL` in `passenger-api.env` - DB reachability from runner host/container network - migration history consistency