Skip to main content
This guide covers the full deployment pipeline for Meridian — from shipping a feature through CI, staging, and production.

Deployment Overview

StageWhat happens
meridian shipModule PRs, lockfile sync
Merge to mainCI runs, lockfile validation
StagingBuild + deploy, Review Apps
Production promoteSame slug, no rebuild
The pipeline is designed for deterministic builds: the lockfile pins each closed-source module by SHA, so every deploy uses the exact same code. Staging and production run the same build artifact.

1. Shipping a Feature

Use the Meridian CLI to ship full-stack features:
meridian ship
This pushes module branches, creates PRs, waits for merges to main, syncs the lockfile, and creates the Meridian PR. You merge all PRs manually. Once the Meridian PR is merged to main, staging deploys automatically.

2. CI Pipeline

On every pull request and push to main, CI runs:
  • Lockfile validation — Each module ref must be a 40-character hex SHA (no branch names or tags)
  • SSH setup — Configures keys for cloning private modules
  • Fetch private deps — Clones each module at its pinned SHA
  • Backend smoke test — Verifies the app can start
If any step fails, the build fails. No deployment occurs.

3. Build Process

When Heroku (or your deploy target) builds the app:
  1. heroku-prebuild — Runs bin/setup_ssh and bin/fetch_private_deps to clone modules at pinned SHAs
  2. npm install — Installs dependencies
  3. heroku-postbuild — Builds the frontend, installs backend deps
The lockfile drives which module commits are used. If fetch_private_deps fails (e.g. SSH keys, invalid SHA), the build fails and no new release is deployed.

4. Staging

  • Auto-deploy — Merging to main triggers a staging deploy
  • Review Apps — PRs can spin up ephemeral environments that build from the branch
  • Verification — Test features on staging before promoting to production

5. Production Promotion

Production does not auto-deploy. You promote from staging when ready.

How Promotion Works

Heroku reuses the staging slug — it does not rebuild. The same build artifact that runs on staging is deployed to production. This means:
  • Deterministic — Production runs the exact same code as staging
  • Fast — No build step during promotion
  • Safe — If staging works, production gets the same binary

How to Promote

  • Dashboard: Pipeline → Production app → “Promote to production”
  • CLI: heroku pipelines:promote -a <staging-app-name>

6. Safety and Downtime

Build failures = no downtime. If config fails (SSH keys, fetch_private_deps can’t clone a module), the build fails and production stays on the current release.
Downtime risk is mainly when the build succeeds and a new release is deployed, but the app crashes on boot (e.g. wrong env vars, DB connection) or has runtime bugs.

Pre-Production Checklist

Before promoting to production (especially the first time or after significant changes):

Lockfile

  • private-deps.lock has 40-character hex SHAs for each module
  • Each SHA exists on origin/main in its module’s repo
  • CI lockfile validation passed on the merged PR

Staging

  • Staging app is healthy and behaving as expected
  • Features that use closed-source modules work on staging
  • No errors in staging logs

Production Config

  • SSH_PRIVATE_KEY and SSH_PRIVATE_KEY_BASE64 are set for production
  • Production has all required env vars (DB, API keys, etc.)
  • Production can reach each module repo (SSH key has access)

Database

  • Schema or migration changes from modules are compatible with production
  • Migrations have been run or are handled by your deploy process

Rollback

  • You have a rollback plan if something fails

Quick Verification

# Lockfile format (each module ref must be 40-char hex SHA)
cd Meridian
node -e "const d=require('./private-deps.lock'); for(const [k,v] of Object.entries(d||{})) { const r=v?.ref; console.log(k, /^[a-f0-9]{40}$/i.test(r) ? 'OK' : 'INVALID', r); }"

# SHA exists on module main (example: Events-Backend)
cd ../Events-Backend
git fetch origin main
git cat-file -t <SHA-from-lockfile>   # should print "commit"