Skip to main content
A deployment builds your app from one commit and rolls it out to an environment on Cloud Run. Meridian orchestrates the whole pipeline; you choose what to deploy and watch it progress.

The pipeline

The tracker shows the same steps live (starting build, building image, running release command, deploying runtime, verifying health), so a stuck deploy is stuck at a named step rather than “in progress”. When a deployment builds nothing, the tracker shows Building as skipped and says why. Reused build means an existing image was deployed: a redeploy of a commit Meridian already built (its image still exists, so it goes straight to deploying) or a promote from another environment. No build: rollback is shown for a rollback.

Release command

A release command is a shell command that runs once per deploy, inside the image that was just built, before the new revision exists. It runs with the same environment variables, secrets, database access and network as the app itself, so npx prisma migrate deploy works there exactly as it would at startup. Set it in the environment’s Runtime settings. The usual value is your migration command. Why move migrations there: the Shopify app template runs prisma generate and prisma migrate deploy in its docker-start script, which means every new instance pays for both before it can answer a request. That is most of a scaled-to-zero cold start. With migrations in the release command and Prisma generated at build time, the start command is just the server, and a cold start drops from over ten seconds to a few. Rules:
  • The command runs through sh -c, so && chains work.
  • It has 15 minutes. A command that has not finished by then fails the deploy.
  • A non-zero exit fails the deploy at the “running release command” step. No revision is created and the live revision keeps serving.
  • The command’s output appears in the deploy log, in place, between “Running release command” and the line that closes the step, whether it succeeded or failed. A failure also links to the full Cloud Run job logs.
  • It runs once per deployment. If Meridian retries a later step, a release command that already completed is not run again.
  • Promotions run it too, since the promoted image is deployed into an environment that has its own database.
Once it is set, remove the migration from your container start command. Keeping it in both places is harmless but wastes the cold start you just saved. Meridian deploys by image digest, never by tag, so a release is exactly the artifact that was built and verified. One deployment can be active per environment, which is what stops two rollouts fighting over the same service.

What starts one

Automatic deployments are a branch plus a target: every push to that branch builds to Development or Production (Staging too on Enterprise). If the target is Development and the app has no development environment, pushes build to Production. Set the target to none and nothing deploys without you asking. A manual deploy picks the environment (production, development, or staging on Enterprise), then the branch and commit. The latest is marked, or you can name any sha. A sha that isn’t pushed to the connected repository fails during the build rather than silently doing nothing. Promote takes a succeeded deployment’s image digest and deploys that same digest into the next environment, with the Bring to staging and Bring to production buttons. There is no build step. Secrets, variables and add-ons stay per-environment. Promoting into production requires the hosting.promote permission. See Bringing a build forward. Pull requests get their own preview deployments, torn down when the PR closes. An app keeps at most two live previews at a time. See Github.

React Router apps: form actions through the edge router

Meridian serves your app through an edge router. The router talks to your Cloud Run service by its internal hostname and passes your public one in X-Forwarded-Host. From React Router 7.18 the framework compares the origin of every form or fetcher POST with the host it believes it serves, and @react-router/serve does not read that header. Left alone, that check would answer 400 to every form your own pages submit. The router closes that gap for you. When a request’s Origin is the public hostname it arrived on, the router forwards it as the origin your app expects, so the framework’s check passes exactly as it would without a proxy. A request from any other origin keeps its Origin and your app rejects it as before. This applies to every hostname your app answers on, custom domains included, with no configuration, no rebuild and no redeploy. You do not need allowedActionOrigins in react-router.config.ts for Meridian hostnames. If you added one, it can stay. Keep it to exact hostnames: a wildcard, or turning the check off, hands any origin the right to post to your actions, which is the attack the check exists to stop. If your app reads the request host itself, read X-Forwarded-Host rather than Host. A framework whose check also compares the scheme, such as SvelteKit on adapter-node, additionally needs to trust X-Forwarded-Proto (for SvelteKit, set PROTOCOL_HEADER=x-forwarded-proto).

Health checks and traffic

A new revision receives traffic only after its health check passes. If it fails, the previous healthy revision keeps serving. There’s no automatic rollback, so a bad release doesn’t take the site down, it just doesn’t take over.

Rollback

A rollback shifts all live traffic back to a previously succeeded deployment, reusing its existing revision, with no rebuild, so it’s as fast as a traffic switch. It’s refused if the target never succeeded or the environment has another deployment in flight. Rolling back development or staging needs Hosting: Edit. Deploying to production and rolling production back both need the hosting.promote permission, the same one that gates Bring to production. Since there’s no restart button, redeploy and rollback are also the two ways to replace running instances. See Restart.

Afterwards

Every deployment keeps its git ref and sha, its image tag and digest, its Cloud Run revision, its event timeline and links to its logs. See Build history for the record and Logs for build and runtime output.
Last modified on October 5, 2026