Files
edr-platform/DEPLOYMENT.md
2026-06-05 11:28:33 +00:00

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-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/<DEPLOY_USER>/environment/edr/<branch-slug>/<project>/

Where:

  • <DEPLOY_USER> defaults to tria (overridable by DEPLOY_USER)
  • <branch-slug> is derived from Git branch (lowercase, non-alphanumeric replaced with -)
  • <project> 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=<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_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:

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_temp from NPM_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-main
  • edr-freight-staging
  • edr-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:

  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=<number> 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