Deploy Pipeline
Fastlane now has two deployment lanes:
- The promotion lane starts from
develop, builds the core apps once, auto-deploysdev, then promotes throughqa,uat,staging, andproduction. - The release lane starts from GitLab Run pipeline for a
release/*branch and deploys the selected lower environment (dev,qa, oruat) without exposing higher-environment jobs.
Full Deploy Flow
Build Once, Deploy Many
Images are built once in the build-images stage and pushed to the GitLab Container Registry ($CI_REGISTRY_IMAGE/<app>:$CI_COMMIT_SHA). Every subsequent deploy job promotes the pre-built image to Heroku by pulling from GitLab CR, retagging for Heroku, and pushing — no Docker build occurs during deploy.
Frontend Runtime Config
Frontend apps (portal, admin) use runtime config injection instead of build-time DOTENV_KEY. The nginx entrypoint generates /config.js from Heroku config vars at container startup, which sets window.__RUNTIME_CONFIG__. Application code reads config via getConfig() from @goosehead-fastlane/runtime-config, which reads from window.__RUNTIME_CONFIG__ in production and falls back to import.meta.env in local dev.
Required Heroku config vars for frontend apps:
| Variable | Description |
|---|---|
VITE_API_GATEWAY_URL | API gateway base URL (e.g. https://dev-fastlane-api-gateway.goosehead.com/api) |
VITE_DA_BASE_URL | DA quote base URL (e.g. https://dev.quote.goosehead.com) |
See Heroku Config Vars Reference for per-environment values.
Deploy Matrix
Each environment deploys apps in parallel using a matrix strategy:
parallel:
matrix:
- APP_NAME: fastlane-portal
HEROKU_APP_NAME: dev-fastlane-portal
PROCESS_TYPE: web
- APP_NAME: fastlane-admin
# ...
- APP_NAME: fastlane-api-gateway
# ...
The promotion lane deploys the 3 core apps (fastlane-portal, fastlane-admin, fastlane-api-gateway) to every environment. The optional build-images-docs path only deploys docs and components to dev.
Promote Script
Script: ci/scripts/promote-image.sh
Each deploy job runs inside a docker:latest image with Docker-in-Docker (dind) and executes:
- Pull from GitLab Container Registry (
$CI_REGISTRY_IMAGE/$APP_NAME:$CI_COMMIT_SHA) - Retag for Heroku (
registry.heroku.com/$HEROKU_APP_NAME/$PROCESS_TYPE) - Push to Heroku Container Registry
- Release via the Heroku Platform API (
PATCH /apps/$HEROKU_APP_NAME/formation) - Health Check — waits 15 seconds, then curls
https://$HEROKU_APP_NAME.goosehead.comexpecting HTTP 200 or 304
Manual Gates and Protected Environments
| Environment | Trigger | Protected Environment Approval |
|---|---|---|
| Dev | Automatic on develop push | None |
| QA | when: manual | QA lead / team |
| UAT | when: manual | QA lead / team |
| Staging | when: manual | PM / tech lead |
| Production | when: manual | Tech lead + PM |
All manual gates use allow_failure: false, meaning the pipeline blocks until the gate is explicitly triggered.
Protected environments are configured in GitLab UI under Settings > CI/CD > Protected Environments.
Release Lane
Use the release lane when you need to validate a release/* branch in a lower environment without entering the normal develop -> dev -> qa -> uat -> staging -> production promotion path.
How It Works
- Open Build > Pipelines > Run pipeline
- Select a
release/*branch - Set
target_envtodev,qa, oruat - Optionally set
run_migrations=trueif you want the matching manual migration job to appear
The pipeline validates the branch/source combination early, builds the 3 core apps once, and exposes only one deploy job:
| Selected Input | Visible Deploy Job | Higher-Environment Jobs |
|---|---|---|
target_env=dev | deploy-dev-release | Hidden |
target_env=qa | deploy-qa-release | Hidden |
target_env=uat | deploy-uat-release | Hidden |
Release-lane deploys are manual for every target environment, including dev. Release-lane migrations are also manual and appear only when run_migrations=true.
Post-Deploy: E2E Tests
The pipeline defines E2E jobs for dev and QA using Playwright with two test projects in parallel, but they are currently disabled in .gitlab-ci.yml to conserve CI minutes:
parallel:
matrix:
- E2E_PROJECT: [da-to-fastlane, carrier-pages]
- Timeout: 1 hour
- Allow failure: true (informational, does not block promotion)
- Artifacts: Playwright HTML report, test output, and JSON results (7-day retention)
- Result Upload: Playwright JSON results are uploaded to the API gateway's test reports endpoint
Scheduled E2E
A scheduled pipeline runs E2E tests against dev every weekday at 7:00 UTC, independent of any deployment. Manual E2E triggers are also available via the GitLab web UI.
Post-Deploy: Database Migrations
The run-migrations-dev job runs automatically after dev deployment when prisma/ files changed.
It creates a one-off Heroku dyno that runs npx prisma migrate deploy:
- Checks if
prisma/files changed in the commit - Creates a run dyno via the Heroku Platform API with a 10-minute TTL
- Polls dyno state every 10 seconds (up to 30 attempts)
- Fetches dyno logs to verify exit status
- Fails the job if the migration exits non-zero or times out
Force Run (Manual Recovery)
For recovery scenarios (for example, a canceled deploy pipeline), you can force this job from New pipeline by setting:
FORCE_RUN_MIGRATIONS=true
When set to true (case-insensitive), the prisma-change gate is bypassed and the job proceeds to run the Heroku migration dyno.
Release-Lane Migrations
Release-lane pipelines do not reuse the automatic prisma-diff migration path from develop.
Instead:
run_migrations=falsekeeps all release-lane migration jobs hiddenrun_migrations=trueexposes the matching manual job for the selected environment- the manual job targets the API app for that environment only
Post-Deploy: Database Seed
The run-seed-dev job is manual only and runs npx ts-node prisma/seed.ts via a one-off Heroku dyno with a 30-minute TTL. It follows the same pattern as migrations but with a longer timeout (60 polling attempts) and allow_failure: true.
Troubleshooting: Skipped Migration Jobs
If run-migrations-dev shows green but logs indicate "skipping", the job detected no prisma/ file changes. If you need to run migrations anyway:
- Open CI/CD > Pipelines > Run pipeline
- Select branch
develop - Add variable:
FORCE_RUN_MIGRATIONS=true - Run the pipeline
Post-Deploy: Sync to Main
After a successful production deploy from develop, the sync-to-main job keeps main in sync:
- Fetches both
mainanddevelop - Attempts a fast-forward merge of
developintomain - Falls back to a merge commit with
[skip ci]if fast-forward isn't possible - Pushes
mainusingGL_PROJECT_TOKEN
This ensures main always reflects what's in production.
Heroku Config Vars Reference
Frontend apps require VITE_* config vars set on each Heroku app. These are read at container startup to generate /config.js.
| Heroku App | VITE_API_GATEWAY_URL | VITE_DA_BASE_URL |
|---|---|---|
| dev-fastlane-portal | https://dev-fastlane-api-gateway.goosehead.com/api | https://dev.quote.goosehead.com |
| dev-fastlane-admin | https://dev-fastlane-api-gateway.goosehead.com/api | N/A |
| test-fastlane-portal | https://test-fastlane-api-gateway.goosehead.com/api | https://qa.quote.goosehead.com |
| test-fastlane-admin | https://test-fastlane-api-gateway.goosehead.com/api | N/A |
| uat-fastlane-portal | https://uat-fastlane-api-gateway.goosehead.com/api | https://uat.quote.goosehead.com |
| uat-fastlane-admin | https://uat-fastlane-api-gateway.goosehead.com/api | N/A |
| preprod-fastlane-portal | https://preprod-fastlane-api-gateway.goosehead.com/api | https://staging.quote.goosehead.com |
| preprod-fastlane-admin | https://preprod-fastlane-api-gateway.goosehead.com/api | N/A |
| prod-fastlane-portal | https://prod-fastlane-api-gateway.goosehead.com/api | https://quote.goosehead.com |
| prod-fastlane-admin | https://prod-fastlane-api-gateway.goosehead.com/api | N/A |
The first deploy with runtime config will fail to connect to the API gateway if these config vars are not set on the Heroku app beforehand.
Teams Notifications
Every deploy job sends an Adaptive Card to Microsoft Teams (if TEAMS_WEBHOOK_URL is configured) with:
- Success/failure status
- App name and Heroku app name
- Branch and commit SHA
- Links to the pipeline and deployed app