GlemorDocs
Engineering

Deployment

Storefront to Vercel, Medusa to Railway, and a release pipeline nobody has to remember to run.

Both apps deploy from main. Nothing is uploaded by hand.

Storefront → Vercel

A standard Next.js deployment of apps/storefront. Push to main and it builds.

Environment, set in the Vercel project:

VariableNotes
NEXT_PUBLIC_MEDUSA_BACKEND_URLThe Railway URL. Unset, the shop serves the static catalogue
NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEYpk_..., printed by the seed script
STRIPE_SECRET_KEYServer-only. Absent, /api/checkout answers 503 with the WhatsApp message
STRIPE_WEBHOOK_SECRETFor POST /api/stripe/webhook
NEXT_PUBLIC_SITE_URLCanonical URL; only a fallback when the request carries no Host header

Live at glemor-storefront.cenvora.dev.

Medusa → Railway

Postgres and Redis are Railway services beside it. apps/medusa/railway.json pins the build and the start command:

{
  "build":  { "builder": "NIXPACKS", "buildCommand": "npm ci && npm run build" },
  "deploy": {
    "startCommand": "cd .medusa/server && npm ci --omit=dev && npx medusa db:migrate && npm run start",
    "healthcheckPath": "/health",
    "healthcheckTimeout": 300,
    "restartPolicyType": "ON_FAILURE",
    "restartPolicyMaxRetries": 3
  }
}

medusa build emits a self-contained server into .medusa/server, which is why both the install and the start happen from inside that directory, and why migrations run on every boot rather than as a separate manual step.

Environment that actually decides behaviour:

VariableWhy it matters
DATABASE_URLPostgres
DATABASE_SSLOpt in with "true". Railway's private network (*.railway.internal) has no TLS; a public Postgres URL does. Defaulting to off means a missing var fails loudly instead of silently disabling certificate checks
REDIS_URLPresent, Medusa uses the Redis cache, event bus and workflow engine. Absent, it falls back to in-memory — right on a laptop, wrong in production, which is why the modules are gated on the variable
MEDUSA_WORKER_MODEshared by default: API and background worker in one process, correct for a single Railway service. Split into server + worker when jobs start competing with request latency
DISABLE_MEDUSA_ADMIN"true" on a worker-only service. Note the spelling — Medusa's own convention. The reversed spelling silently never matches
STORE_CORS, ADMIN_CORS, AUTH_CORSRequired, non-null-asserted in the config
JWT_SECRET, COOKIE_SECRET

Live at glemor-admin.cenvora.dev, admin at /app.

Releases

.github/workflows/release.yml runs on every push to main, with concurrency: release so two runs cannot race to compute "the next version" from the same tag. It checks out with fetch-depth: 0release.mjs walks history back to the last tag, and a shallow clone would silently produce a wrong version.

The workflow runs the three gates as three separate steps, then the release tooling self-test, then node scripts/release.mjs --tag, which:

  1. computes the next semver from the Conventional Commits since the last tag;
  2. writes CHANGELOG.md — every commit, developer voice;
  3. writes apps/docs/content/changelog/<version>.mdx — the Release-Note: trailers, plain language;
  4. bumps package.json, commits, tags, and publishes the GitHub release.

Both changelogs are generated. Neither is ever hand-edited — see Changelog.

Docs

This site is a plain Next.js app (npm run docs:build) and can deploy anywhere Next runs. It has no runtime dependencies: every page is built from files in the repository.

On this page