5.6 KiB
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-apifreight-portalfreight-backofficepassenger-apipassenger-portalpassenger-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/<DEPLOY_USER>/environment/edr/<branch-slug>/<project>/
Where:
<DEPLOY_USER>defaults totria(overridable byDEPLOY_USER)<branch-slug>is derived from Git branch (lowercase, non-alphanumeric replaced with-)<project>isedr-freightoredr-passenger
Required files per project
For edr-freight:
freight-api.envfreight-portal.envfreight-backoffice.env- optional:
freight-web.build.env
For edr-passenger:
passenger-api.envpassenger-portal.envpassenger-backoffice.env- optional:
passenger-web.build.env
Required env key
Each service env file must contain:
PORT=<number>
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:
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_PORTPASSENGER_API_PORTFREIGHT_PORTAL_PORTFREIGHT_BACKOFFICE_PORTPASSENGER_PORTAL_PORTPASSENGER_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/portalandapps/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:
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=<project>-<branch-slug>
- Creates
.npmrc/.npmrc_tempfromNPM_TOKEN. - Runs:
docker compose --project-name "$COMPOSE_PROJECT_NAME" build <service>docker compose --project-name "$COMPOSE_PROJECT_NAME" up -d <service>
- Cleans
.npmrc/.npmrc_temp.
Branch/Environment Isolation
Compose project name is generated as:
<project>-<branch-slug>
Examples:
edr-freight-mainedr-freight-stagingedr-passenger-dev
This prevents container/network/volume name collisions between branches.
Local Manual Deployment (Optional)
From repo root:
DOCKER_BUILDKIT=1 docker compose build <service>
docker compose up -d <service>
If private packages are required locally, create .npmrc:
cat <<EOF > .npmrc
@tria-plc:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=<YOUR_TOKEN>
always-auth=true
EOF
Passenger API Startup Behavior
Passenger container entrypoint runs on startup:
npm run prisma:generatenpm run prisma:migrate(deploy mode)npm run prisma:seed- 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=<number>to that service env file.
Private package install fails
Check:
NPM_TOKENexists in repo secrets.- Workflow created
.npmrcsuccessfully.
Prisma seed/migrate failures (passenger)
Check:
DATABASE_URLinpassenger-api.env- DB reachability from runner host/container network
- migration history consistency