Skip to content

Staging and rollback

A deployment that fails to build never goes live, so a broken file costs you a failed deployment rather than an outage. A deployment that builds and behaves wrongly is another matter. This guide sets up the three things you want before that day: a staging service to catch it, a rollback to undo it, and a way around GraphPilot if the problem is GraphPilot. What a deployment is and what happens while it runs is on deployments.

The running example is shop-api in production, with its origin at https://api.example.com, and a staging origin at https://staging-api.example.com. Both deploy from the same repository, where schema.graphql and gpilot.toml are versioned next to the application.

  • The gpilot CLI, logged in, and access to the service’s Deployments in the portal.
  • A plan that includes more than one service, for the staging service. See pricing.
  • Your configuration files in version control, unless gpilot.toml holds a JWT provider’s shared secret (see below).

There is no promotion between environments: a deployment builds from the files in front of it. A staging environment is therefore a second service, with its own subdomain and its own origin:

Terminal window
gpilot service create \
--name "Shop API staging" \
--subdomain shop-api-staging \
--url https://staging-api.example.com \
--environment staging

--environment is a label (production unless you set it) that tells the two services apart in lists. It changes nothing about how the service runs.

The two services share nothing: separate cache, separate keys, separate telemetry. That is what makes a staging deployment a real rehearsal, and it also means a purge on one never touches the other.

Deploy the same files to both, staging first:

Terminal window
gpilot deploy --service shop-api-staging --yes
# verify against https://shop-api-staging.k7m2qp.graphpilot.cloud, then
gpilot deploy --service shop-api --yes

In CI, run the first line on pushes to a staging branch and the second on main; one organization API key can deploy both. See deploy from CI, and the recipe promoting from staging in CI for one workflow that deploys staging on every merge and production after an approval.

Verify against staging what the change was meant to do: the gp-cache status and gp-cache-reason of the requests it affects, and the deployment’s findings in the portal. See findings.

To go back, redeploy the version that worked.

In the portal, open Deployments, open the deployment that was good and choose Redeploy. The dialog starts with that deployment’s schema and configuration selected, so the new deployment builds from exactly those two files. It runs on the service’s runtime pin, or the stable version when the service has none, unless you pick another version under Proxy version. The version the good deployment ran on is marked same as this deployment there, as long as it has not been withdrawn.

From the CLI, deploy the previous files again:

Terminal window
git checkout <previous-commit> -- schema.graphql gpilot.toml
gpilot deploy --service shop-api --yes

Keep both files in version control either way: git tells you why a version changed, which the deployment list does not. The exception is a gpilot.toml that holds a JWT provider’s shared secret, which the file carries as written: keep that file out of the repository, or use a provider with a public key.

Rolling back to an older runtime has a time limit. After some runtime versions, an older runtime is refused seven days after the service first went live on the newer one, with a message naming the version to use:

This service can no longer run runtime <version>. Deploy runtime <minimum> or later.

If no runtime the service can run is registered yet, the message ends with “No runtime it can run is registered yet; contact support.” instead.

A service pinned to an older runtime within those seven days keeps what that runtime needs, so a rollback you pin in time is not cut off. A rollback of the schema and configuration on the current runtime is never affected.

If the bad version was wrong about caching rather than about serving, entries it stored keep their lifetime and partitioning after the rollback, because the cache survives a deployment. Purge them once the rollback is live:

Terminal window
gpilot purge --all --yes --service shop-api

4. Know how to take GraphPilot out of the path

Section titled “4. Know how to take GraphPilot out of the path”

If GraphPilot itself is the problem, the way out is where your clients point, not a setting.

  • On a custom domain, point the DNS record back at your origin. Clients need no change, and the switch takes effect as your DNS TTL allows. Keep that TTL short.
  • On the service hostname (shop-api.k7m2qp.graphpilot.cloud), clients have to be pointed at your origin directly. Make that endpoint configurable in your clients before you need it: it is the difference between a configuration change and a release.

Either way your origin then serves the full, uncached load. Size for it.

After each deployment, the newest entry in the list is completed (the CLI writes statuses in lowercase, in the table and in -o json):

Terminal window
gpilot status --service shop-api-staging
gpilot status --service shop-api

After a rollback, send a request the bad version answered wrongly with curl. Once you have purged, the first answer is a MISS from your origin under the restored configuration, and the gp-cache status and gp-cache-reason match what the good version reported. A HIT before the purge can still be an entry the bad version stored.

Rehearse step 4 once on staging: switch a test client to the staging origin directly and back.

  • A redeploy uses the service’s runtime pin, else the stable version, unless you choose another. If you suspect a GraphPilot change, pick what the good deployment ran on under Proxy version (marked same as this deployment).
  • Staging keeps its own cache. A HIT on staging says nothing about production’s entries, and staging’s hit rate reflects staging’s traffic.
  • An origin locked to GraphPilot refuses direct traffic. If your origin accepts only GraphPilot’s IP ranges or only signed requests, step 4 needs a way to relax that. Decide it in advance.
  • A long DNS TTL turns a five-minute exit into an hour. Lower it before an incident, not during one.