Troubleshooting guide

Deploy succeeded but the live site still shows old content

You push, the pipeline reports success, and the live site still shows the old version. Or the deploy never starts, hangs, or lands before the backend it depends on.

13 min read Updated 8 Oct 2026Last reviewed 10 Oct 2026How we collect cases

What you'll see

  • The build log ends in success, but the live site shows content from before your commit.
  • A deployment fails before the build starts, and you see an error message on the deployment instead of build logs.
  • The build hangs, is cancelled at a time limit, or stops with an out-of-memory or disk-space message.
  • Pushes to the repository start no deployment, or deploy jobs stay pending.
  • After a deploy, stylesheets or script chunks return 404, or some users see chunk load errors while others do not.
  • The new frontend is live but calls a route or database column that does not exist yet.

Why it happens

Cause 1

A green step is not proof that the new commit is serving

Success usually means the build finished and the files uploaded. It does not mean production traffic points at that deployment. Browsers and shared caches follow Cache-Control: max-age keeps a response fresh without asking your origin, and no-cache allows storing but forces revalidation before reuse. Your own post-deploy check often proves nothing either, for example when it tests the first item in a list instead of something the new commit changed.

Cause 2

The host holds the deployment back

On Netlify, a locked deploy is still built but not published, and a new Git-triggered deploy overwrites a manual rollback while auto publishing is on. Elsewhere an alias or branch address can keep pointing at an older deployment. In each case the build is fine and the pointer is wrong.

Cause 3

The deploy fails or never starts before your build runs

Vercel documents builds that fail without logs because a precondition is missing: an invalid vercel.json, an Ignored Build Step, a commit from someone who is not a team member, or a Marketplace resource that failed to provision. On other hosts a missing repository webhook or an account-level flag has the same effect. You get an error on the deployment, not a log to read.

Cause 4

The build environment differs from your machine

Unpinned dependencies can pull a new major version that your code was never tested against. Build containers also have fixed memory and disk, and Vercel cancels a build that exceeds them or runs past 45 minutes. The build cache key on Vercel includes the Node.js version, package manager and Git branch, and a failed build does not update the cache.

Cause 5

Frontend, backend and migrations land out of order

If the frontend deploys first, it calls routes or columns that are not there yet. If migrations run from a machine that does not have the production settings, or the migration tool is pruned from the build image, the step is skipped or fails. Version skew is a separate failure: a user with the old page open requests a script chunk name from a deployment that has been replaced.

How to fix it

  1. Put a version marker in every build

    Write the commit hash to a static file at build time, so the file changes with every commit. Vercel also exposes the commit hash as a system environment variable, but a file does not depend on the host. Serve the file with Cache-Control: no-store so the check cannot read a cached copy.

    {
      "scripts": {
        "build": "git rev-parse HEAD > public/version.txt && next build"
      }
    }
  2. Make the post-deploy check ask for that marker

    Request the file from your public domain, not the deployment's own address, and compare it with the commit you just shipped. Retry for a few minutes, then fail the pipeline with both values in the message.

    want=$(git rev-parse HEAD)
    for i in $(seq 1 30); do
      got=$(curl -fsS "https://example.com/version.txt" || true)
      [ "$got" = "$want" ] && exit 0
      sleep 10
    done
    echo "serving ${got:-nothing}, expected $want" >&2
    exit 1
  3. Install from the lockfile

    npm ci requires a package-lock.json or npm-shrinkwrap.json, never writes to either, and exits with an error if they disagree with package.json. Use it as the install command in your pipeline so the build installs the versions you tested.

    npm ci
  4. Run migrations as an explicit, ordered step before the app deploys

    Apply pending migrations from the pipeline, not from a laptop, and make the app deploy depend on that step succeeding. Prisma's docs describe prisma migrate deploy for this and note that the prisma package must be available when it runs, which pruned dev dependencies can break. Keep each migration additive, so the previous release still works against the new schema.

    npx prisma migrate deploy
  5. When nothing deploys, inspect the deployment, not the repository

    Open the failed or missing deployment and read the error on it, since some failures happen before any build log exists. For a missing trigger on GitHub, open Settings, Webhooks, then Recent deliveries: it lists the last 3 days with the response your host returned, and an admin can redeliver. If a build looks stale or stuck, redeploy without the build cache: leave 'Use existing Build Cache' unticked on Vercel, or choose 'Retry without cache with the latest branch commit' on Netlify.

  6. Cache by file type and protect open tabs

    Give hashed asset filenames a long lifetime and the page that references them a short one, so a new deploy is picked up on the next load. Vercel's Skew Protection (Pro and Enterprise) adds the deployment id to framework-managed requests, so an open page keeps loading assets from its own deployment; the default maximum age is one day.

    # hashed filenames, e.g. /assets/app.3f9a1c.js
    Cache-Control: public, max-age=31536000, immutable
    # the page that references them
    Cache-Control: no-cache
  7. Rehearse a rollback once

    On Vercel, vercel rollback points production at an earlier deployment without rebuilding. On the Hobby plan only the immediately previous production deployment is available. On Netlify, 'Publish Deploy' republishes an earlier successful deploy, and locking deploys stops the next Git push overwriting it.

    vercel rollback <previous-deployment-url-or-id>

Check it's fixed

  • Request the version marker from your public domain and confirm it equals git rev-parse HEAD for the commit you shipped.
  • Load the site in a private window with the network panel open: every stylesheet and script returns 200, and the HTML response carries the Cache-Control header you set.
  • Push an empty commit and confirm a new deployment lists that commit, and that the repository webhook shows a successful recent delivery.
  • Roll back, confirm the marker shows the previous commit, then roll forward and confirm it shows the new one.

Fix it with Gemmein

On Gemmein, every publish answers PUBLISHED or FAILED and lands on the private Development site, and site_status reads both addresses and the version each serves. The Live site changes only when you approve a promote, and the docs set the order for data changes: promote first, then deploy the frontend that depends on them.

  1. Build on your computer, then publish the output

    Gemmein does not build the app: your computer runs the build, and npx gemmein publish sends the output. Every publish lands on the Development site and answers PUBLISHED or FAILED.

    npm run build
    npx gemmein publish
  2. Open the preview with its link and read which version serves

    The Development site is a private preview: open it with its link, which every publish, site_status and the dashboard's Open preview hand out, because the address on its own, with no link, shows "This preview is private". site_status reads both addresses, the version each serves and every version, and an open preview tab reloads by itself after each publish.

  3. Promote data changes before the frontend that needs them

    After go-live, new collections and changes to a live collection's declared fields reach Live only by promotion, through npx gemmein go-live again or the dashboard's Go-live page. The docs say to promote first, then deploy the frontend that depends on the new collection or field, because a frontend shipped ahead of its promotion reads blanks or refusals from live.

    npx gemmein go-live
  4. Approve the promote to put the version live

    Once the app is live, site_promote (or npx gemmein site promote <version>) asks for approval to put one Development version on the Live site, the same files, copied; it is approved in the browser with the code Gemmein emails, and approving it carries it out, once. Before go-live it is refused (not_live): there is only the Development site.

    npx gemmein site promote <version>

Questions

Why does my deploy say success but the site shows old content?

Success means the build finished and the files uploaded, not that your domain serves them. A browser or shared cache may hold the old page under max-age, or the host may still point production at an earlier deployment. Compare a version marker from your public domain with the commit you shipped.


Should I use npm ci or npm install in the pipeline?

Use npm ci. It installs from the lockfile, never writes to it, and exits with an error if the lockfile and package.json disagree. npm install can resolve newer versions than you tested.


Does redeploying without the build cache fix a stale site?

It fixes a stale or corrupted build cache, for example with the Redeploy option on Vercel or 'Retry without cache' on Netlify. It does not fix an edge cache holding old HTML or an alias pointing at an old deployment. The version marker tells you which of these you have.


How do I stop chunk load errors after a deploy?

They come from version skew: an open page asks for a script chunk that the new deployment no longer has. On Vercel, Skew Protection pins framework-managed requests to the page's own deployment. On other hosts, keep the previous hashed assets available for a while and reload the page when a chunk fails to load.


Does a rollback undo a database migration?

No. A rollback changes which code serves traffic, and on Vercel it does so at the routing layer without rebuilding. Keep migrations additive so the previous release still works against the new schema.


Sources

Last reviewed 10 Oct 2026. What changed: No changes: steps re-checked against 8 official page(s). Checked against: docs.npmjs.com, vercel.com, vercel.com, vercel.com, docs.netlify.com, prisma.io, docs.github.com, developer.mozilla.org. How we check this

← Back to the full report