Skip to content
Last updated Give Feedback

Upgrade procedure

This is the supported flow for moving an installation from one tagged release to the next. The flow assumes:

  • The customer is on the Customer Onboarding layout (or the Manual install (advanced) equivalent).
  • The current install is healthy — /health returns {ok:true} for all four services before starting.
  • There is a recent backup (see Backup & Restore). The pre-upgrade snapshot in step 1 below is mandatory regardless.

If your install was set up via Docker Compose, the same logic applies — substitute docker compose pull && docker compose up -d for the manual pm2 reload all step.

Semver policy (what each version bump means)

Section titled “Semver policy (what each version bump means)”

ecommus follows semver across both the framework and the public plugin/theme contracts:

BumpWhat changesCustomer action
PatchBug fixes, security patches, internal refactors. No public-API changes.Upgrade any time. No code changes on customer side.
MinorNew features, additive plugin/theme contract additions, deprecations.Read the release notes for new opt-in features and deprecations to migrate off of.
MajorBreaking plugin / theme / route / config contract changes.Read the migration guide for that release before bumping. Test in staging first.

Stable surfaces (semver-protected — no breaking changes within a major):

  • @ecommus/plugin-sdk types + PluginContext shape
  • @ecommus/theme-sdk manifest schema + slot map
  • @ecommus/client REST client method signatures
  • License JWT shape (packages/core/src/license.ts)
  • Public REST routes (/api/admin/*, /api/storefront/*, /api/super-admin/*)
  • Webhook payload shapes for issued events

Internal surfaces (no semver guarantee — may change in any release):

  • Service helpers under apps/api/src/services/
  • Drizzle schema details (always go through the migration runner, never read tables directly from outside apps/api)
  • Internal events not exposed via the EventBus public types

Run all of these in the listed order. Aborting at any step is fine — the install is unchanged until step 4.

Terminal window
# 1.1 Confirm current install is healthy
curl https://api.mystore.ro/health
curl https://admin.mystore.ro/api/health
curl https://superadmin.mystore.ro/api/health
# 1.2 Check the release notes for the target version
# -> https://docs.ecommus.ro/whats-new/
# -> https://github.com/MDLABS-cmd/ecommus/releases/tag/<v>
# If MAJOR: read the migration guide LINKED FROM the release notes
# If MINOR: scan the deprecation list
# 1.3 Take a database snapshot
pg_dump -Fc ecommus_prod > /backup/pre-upgrade-$(date +%F-%H%M).dump
# 1.4 Capture the current installed plugin set (for diff after upgrade)
node --experimental-strip-types apps/api/src/cli/list-plugins.ts > /backup/plugins-pre.txt
# 1.5 Stop the workers gracefully — prevents in-flight jobs landing on
# a half-upgraded code+schema combo
pm2 stop ecommus-api-workers # or: ECOMMUS_WORKERS=false on the running instance
# 1.6 Notify operators (super-admin → settings → maintenance window)

The snapshot in 1.3 is the rollback point. Verify it was created (ls -lh it) before proceeding.

Terminal window
git fetch --tags
git checkout v<new-release> # e.g. v1.4.0
npm ci # license-bound npm token resolves @ecommus-plugin/* updates too

If npm ci fails with 403 forbidden on @ecommus-plugin/<x> or @ecommus/theme-<x>:

  • Your NPM_REGISTRY_TOKEN is missing or revoked. Re-fetch from your customer portal.
  • Your license expired. Renew via super-admin or contact your account manager.
  • The plugin is no longer on your license. Either re-issue the JWT or stay on the previous release.
Terminal window
npm --workspace @ecommus/storefront-astro run build
npm --workspace @ecommus/admin run build
npm --workspace @ecommus/super-admin run build
npm --workspace @ecommus/api run build

If any build fails, git checkout v<previous-release> and npm ci to revert. Builds are idempotent — re-running is safe.

Migrations run before the new code is live. Two phases:

Terminal window
# Core framework migrations (apps/api owns these)
node --experimental-strip-types packages/db/src/migrate.ts
# Plugin migrations (collected by each plugin via ctx.registerMigration())
node --experimental-strip-types apps/api/src/cli/run-plugin-migrations.ts

Both runners are idempotent. They write to _ecommus_core_migrations / _ecommus_plugin_migrations and skip already-applied entries. A failed plugin migration logs the failure and continues — fault-tolerant per CLAUDE.md §0.4.

If any migration fails:

  1. Don’t reload pm2. The old code still runs against the partially-upgraded DB.
  2. pg_restore the snapshot from step 1.3 (see Backup & Restore for the procedure).
  3. File a bug with the failing migration name + log excerpt.
Terminal window
pm2 reload all

pm2 reload is graceful — in-flight requests finish on the old process before the new one takes over. There’s no observable downtime under normal load.

If the install is single-process (no pm2 cluster mode), use pm2 restart all instead — there’s a brief 1-2 second outage. Acceptable for non-customer-facing maintenance windows.

Terminal window
# 6.1 Re-enable workers
pm2 start ecommus-api-workers # or: ECOMMUS_WORKERS=true and pm2 restart api
# 6.2 Health
curl https://api.mystore.ro/health
curl https://admin.mystore.ro/api/health
curl https://superadmin.mystore.ro/api/health
# 6.3 Plugin sanity
node --experimental-strip-types apps/api/src/cli/list-plugins.ts > /tmp/plugins-post.txt
diff /backup/plugins-pre.txt /tmp/plugins-post.txt # expect: only version bumps
# 6.4 Hit one admin route end-to-end (forces a tenant-scoped query)
curl -H "Authorization: Bearer <admin-jwt>" https://api.mystore.ro/api/admin/products?limit=1
# 6.5 Hit one storefront route (forces a host-resolved tenant)
curl https://mystore.ro/api/storefront/categories

If anything is off, see Rollback below.

  • Read the release notes for any manual post-upgrade steps (e.g. “rotate JWT_KEY_ID”, “re-import niche-package preset”).
  • Acknowledge the maintenance window in super-admin.
  • If the upgrade introduced new env vars, add them now and pm2 restart api (re-read of .env).
  • For major-version upgrades, monitor error rates for the next 24 h via the observability stack (SENTRY_DSN + Prometheus).

Use the snapshot from step 1.3.

Terminal window
# 1. Stop the apps
pm2 stop all
# 2. Restore the DB
pg_restore --clean --if-exists --no-owner --dbname=ecommus_prod \
/backup/pre-upgrade-<timestamp>.dump
# 3. Check out the previous release
git checkout v<previous-release>
npm ci
# 4. Re-build
npm --workspace @ecommus/storefront-astro run build
npm --workspace @ecommus/admin run build
npm --workspace @ecommus/super-admin run build
npm --workspace @ecommus/api run build
# 5. Start
pm2 start all
# 6. Verify
curl https://api.mystore.ro/health

Rollback is destructive on the data side — any rows written between the snapshot and the rollback are lost. Avoid running rollback on a live store unless the alternative is worse (data corruption, security exposure).

When the release is a major bump:

  1. Test in staging first. Stand up a staging install pointed at a snapshot of prod, run the full upgrade flow, run the smoke tests. Only then promote to prod.
  2. Read the migration guide linked from the release notes. Major releases ship a MIGRATION-Vx-to-Vy.md doc per release in /docs/08-changelog/.
  3. Audit your custom plugins. If your store carries internal plugins (i.e. not from npm.ecommus.cloud), the plugin contract may have changed. The migration guide lists every removed export, renamed type, and signature change.
  4. Audit your theme overrides. Theme slot maps may have new required slots or removed deprecated ones.

If the framework wants you to drop a deprecated config key (e.g. legacy JWT_SECRET removed in v2.0), the migration guide lists the new equivalent and the boot-time fail-fast that catches the omission.