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"
}
}
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
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
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
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.
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
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>