Chapter 5. Deployment and operations
Learning objectives
- Explain the conditions that create preview and production deployments and the purpose of each.
- Configure environment variables per environment and understand when changes take effect.
- Create and use pre-deployment and post-deployment checks suited to your app.
- Narrow an incident's time and location through logs, and restore the last known good version first.
- Distinguish what changes after rollback from what rollback does not restore, including data and configuration.
Introduction
At 7 p.m. on Friday, a solo developer deploys a new feature to a booking app. Ten minutes later comes a message: "I cannot sign in." There are two possible responses: keep repairing production until the cause is found, or return to yesterday's working version within a minute and then investigate calmly. The first prolongs users' disruption, and a rushed fix can cause a second incident.
Deployment marks the beginning of operations. From that moment, real users, data and costs are involved. Three preparations must accompany it: a way to check under user-like conditions before release, a way to notice problems and a way to return to an earlier state.
This chapter covers those preparations using Vercel, a common choice for individual developers. It explains previews and production, environment variables, checks before and after deployment, logs and monitoring, rollback and incident response. Going deeper examines data changes, gradual rollouts and free-plan operating constraints. The principles apply across platforms.
Key concepts
What deployment does
Deployment means putting the app that ran on your own computer onto the internet so anyone can use it. Production, or the live environment, is the real shop receiving customers, while a preview is a rehearsal space where you invite acquaintances to look around before opening. When you find a problem during rehearsal, customers do not have to experience that problem.
Vercel's documentation defines a deployment as "the result of successfully building a project" and explains that every deployment gets its own unique URL. You can create a deployment through a Git push, the CLI, a deploy hook or the REST API. The important property is that each individual deployment is stored separately. Even after you create a new deployment, the previous one stays as it was until it is deleted under the retention policy. Only which deployment the domain points to changes. This is what makes a fast rollback possible.
Preview and production
The default behavior confirmed in Vercel's documentation is as follows.
| Situation | Result |
|---|---|
Push to a branch other than the production branch (usually main) |
Preview deployment |
| Create a pull request | Preview deployment |
Run vercel without --prod |
Preview deployment |
Push or merge to the production branch, or run vercel --prod |
Production deployment |
| First deployment of a new project | Always production, regardless of branch and even without --prod |
The first-deployment rule has a surprising consequence. Even if you first connect and deploy from a feature branch, that deployment becomes production. This is designed so a new project can immediately receive a production domain.
There are two types of preview URL. A branch URL continues to point to the latest deployment of that branch, while a commit URL points to the deployment of a particular commit and stays fixed there. Use a branch URL when you want to show "the latest work at all times," and a commit URL when comparing or recording a particular point in time. On Pro and Enterprise plans, you can also create separate custom environments such as staging.
Sharing a preview
A preview is a rehearsal space, but it is a space open on the internet. If anyone who knows the URL can enter, the data and features visible in the preview are exposed as they are. Vercel's Deployment Protection controls who can access deployment URLs. According to the documentation, the recommended scope, Standard Protection, protects all deployments except production domains and is available on every plan. Among the protection methods, Vercel Authentication permits only Vercel users with the appropriate permissions to enter, while password protection is not available on the Hobby plan.
Follow three preview principles.
- Use test keys and a test database for previews. Ensure that no real payments are made and no real emails are sent.
- When showing the preview to someone outside the team, use the access methods provided by the platform rather than turning off protection.
- Before sharing, check that no real personal information is visible on the preview screens.
Environment variables differ by environment
Vercel keeps environment variables separate for Production, Preview, Development (local) and each custom environment. Even variables with the same name can have different values in each environment; for example, you can put test payment keys in previews and real payment keys in production. Preview variables can also have different values for a particular branch, and a branch-specific value overrides the general Preview value with the same name. Download values for local development with vercel env pull, but first check that the env file you receive is included in .gitignore. For each variable, you can also choose between the Config type (readable by members with permission) and the Secret type (write-only after it has been saved).
Caution
Vercel's documentation says environment variable changes affect only new deployments, never existing ones. Always redeploy after changing them. This commonly explains "Why is it unchanged after I updated the value?" Values embedded during builds, such as Chapter 4's public-prefixed variables, particularly require a new build.
If a new feature uses a new environment variable, check that the variable is set in every environment that needs it. If you add it only to Preview and forget Production, a feature that worked in the preview fails in production; if you do the reverse, it fails in the preview.
Development servers are different from builds
It is common for an app that ran well on the development server (npm run dev) to fail during deployment. Deployment goes through a build (npm run build) rather than the development server, and some errors appear only at the build stage. A type error may stop the build, a page rendered in advance during the build may try to read data that does not yet exist, or an environment variable needed at build time may be missing. Before deployment, run the build command once locally as well.
In Vercel, build logs appear on the deployment screen; runtime logs, records from the running app, appear under Logs. If the build succeeds but one feature returns 500, inspect runtime logs.
Pre-deployment checks
| Check | Question |
|---|---|
| Automated checks | Have tests, type checks and the build all passed? |
| Preview | Have you personally completed core flows (sign-up, payment, saving) at the preview URL? |
| Environment variables | Are new variables also set in Production? |
| Data | If the database schema changed, is it compatible with old code too? |
| External integrations | Do payments, email and webhooks point to production addresses and keys? |
| Recovery | Do you know the last known good deployment to return to? |
Record both verified and unverified items on the checklist, not just checkmarks (Chapter 2).
Checks immediately after deployment
An indicator saying the deployment succeeded means only that the build finished; it does not mean users can use the app. A smoke test is a quick check of the core flows within a few minutes immediately after deployment. The term comes from checking whether a new machine starts emitting smoke when you turn it on. Connect to the production address, sign in, run one core feature, refresh the page to confirm the saved result, and check that there are no new errors in the console or runtime logs.
To inspect a deployment with production settings before exposing it to users, use Vercel's staged production deployment. The documentation says to open Production's Branch Tracking in Environments settings and disable Auto-assign Custom Production Domains. Production-branch deployments then wait for manual promotion rather than immediately receiving production domains. Check the generated deployment URL, then promote it: the domain moves to that deployment without rebuilding. Remember that it uses production environment variables and therefore actually reaches production services and data.
Logs and monitoring
Logs are the records an app leaves while it runs, and monitoring means continually watching metrics such as the error rate and response time. They have different roles. Monitoring tells you "something has gone wrong," while logs tell you "what went wrong and where."
Vercel runtime logs display records of function invocations and console.log output grouped by request. According to the documentation, requests with 400-level status codes are marked as warnings, and requests with 500-level codes as errors. You can filter by path, status code, environment, branch, deployment and other fields. The CLI command vercel logs shows the latest 100 entries from the most recent 30 minutes by default. To look further back, widen the range with --since; to view only a particular deployment, specify it with --deployment.
When an incident occurs, establish three things from the logs before guessing at a fix: when it started, which requests were affected, and what error occurred. If the time when errors increased coincides with the deployment time, that deployment is the most likely cause.
Logs have a retention period, so they may already be gone if you look too late. Set up external checks that periodically check core URLs, alerts that arrive when the error rate rises, and external storage to receive records you need to retain longer. Monitoring is complete only when you have also decided who will do what when an alert arrives.
Rollback: returning to the last known good version
A rollback replaces a problematic deployment with an earlier working one. Vercel's Instant Rollback immediately points domains back to an earlier production deployment without rebuilding. Official documentation gives these cautions.
- The restored deployment uses its original configuration. Later project environment variable changes do not apply, and cron definitions return to that deployment's versions.
- Rollback disables automatic production-domain assignment. New production-branch commits do not move the domain away from the restored deployment. Use Undo Rollback in the dashboard or
vercel promoteto promote another deployment and resume the normal flow. - Hobby can roll back only to the immediately preceding deployment. Pro and Enterprise can select any deployment previously assigned to a production domain. A preview never assigned to production is ineligible.
- Rollback confirmation reminds you of changes to external APIs and databases. Rollback restores code, not data.
Platform rollback does not change Git history. The problematic commit remains on main; add a fix or revert it so the next deployment does not reintroduce the problem (Chapter 3).
Incident response order
Incident response resembles emergency care: stop the bleeding before establishing the cause.
- Assess impact. Which feature has failed, since when and for how many users?
- Restore service. If a recent deployment is the likely cause and database structure was unchanged, roll back to the last known good deployment.
- Find the cause. Narrow it using runtime logs and the diff. Work on a branch, not directly on production.
- Fix and verify. Check the fix in a preview.
- Resume normal operation. Promote the fixed deployment to restore automatic assignment and run a smoke test.
- Record the incident. Explain what happened, why and what will prevent recurrence. Even one added checklist line is useful.
Going deeper
Code returns, data does not
Rollback feels like magic only when it comes to code. If the new deployment renamed or deleted a database column, the old code restored by rollback fails when it looks for the column that is no longer there. The old code may also be unable to read data written by the new code in a new format. It is like turning back the clock while the parcels delivered in the meantime remain where they are.
For this reason, split changes to the data structure across several deployments so the app will not break if you roll back at any point. This is commonly called the expand–contract approach.
- Expand: add the new column, keeping the old one.
- Write both: new code writes to both columns and prefers the new one for reads, falling back to the old one.
- Migrate: copy existing data into the new column.
- Switch: change code to read and write only the new column.
- Contract: delete the old column after confirming no code uses it.
Each stage can run alongside the code from the immediately preceding stage, so the data and code remain compatible whichever deployment you roll back from. Do the deletion last, after you are confident that there will be no need to go back. Before a deployment that deletes or overwrites data, separately back up the data and verify that it can be restored. If previews and production share the same database, avoid a configuration that runs migrations during the build: merely building a preview could change production data.
Release gradually
You do not have to expose a new version to every user at once.
A feature flag is a way to include a feature in the code but determine whether users see it through an on/off switch. You can frequently merge even unfinished features into main while keeping them switched off, reducing the merge conflicts that come from long-lived feature branches. If a problem occurs, you turn off the switch without deploying.
For a deployment that changes the whole app together rather than just one feature, divide users between deployments. Vercel's Rolling Releases, available on Pro and Enterprise, initially sends only a portion of visitors, such as 10%, to the new deployment and keeps the rest on the previous one. It lets you compare the metrics of the two deployments side by side. You can increase the rollout in stages until it is promoted to 100%, or use Instant Rollback to return if a problem appears.
Deployment changes can also cause version skew: the browser retains old client-side code while the server runs the new version. Vercel describes errors from client and server running different versions and says Skew Protection (Pro and Enterprise) keeps clients communicating with servers from the same deployment. Pinning applies to framework-managed requests such as static assets, navigation and Server Actions; custom fetch() requests need a deployment ID added separately. It is enabled by default for supported-framework projects created on those plans after November 19, 2024, and recommended alongside Rolling Releases.
Operating constraints for small apps
Apps starting on free plans also face operational tool limits. Without checking, you may discover them only during an incident. Representative limits from Vercel's documentation at the time checked are below.
| Item | Hobby | Pro |
|---|---|---|
| Runtime log retention | 1 hour | 1 day (30 days with Observability Plus) |
| Instant Rollback range | Immediately preceding deployment | Deployments previously assigned to production |
| Minimum cron interval | Once a day | 1 minute |
| Cron timing precision | Somewhere within 59 minutes of the specified hour | Minute-level |
Short retention makes alerts and external records more important. Hobby cron can run anywhere within 59 minutes of the specified hour, so it is unsuitable for precise timing. Limits change: rather than memorize figures, compare your app's operational needs with official plan documentation.
At work
Starlight Observatory: before assigning the domain
Jisu, operator of "Starlight Observatory," which takes bookings for observation experiences, set a release date for its first deposit-payment feature. Test payments worked in preview, and tests and builds passed. She paused before merging to main: previews used test keys, but production would use new live payment and text-message keys. Nobody had checked that combination.
Jisu considered three options.
- Merge immediately: assume preview success means production success. But variables and integrations differ; the first real customer becomes the tester. Rejected.
- Test on the production domain and roll back if needed: possible, but customers also use the same live interface during testing. Rejected.
- Staged production deployment: deploy with production configuration but defer domain assignment. Selected.
Jisu disabled Auto-assign Custom Production Domains under Production's Branch Tracking and merged to main. A production deployment appeared while the live domain stayed on the old one. A small real payment through the generated URL successfully created a booking, but no confirmation text arrived. Runtime logs showed a missing API key at the text-sending step: SMS_API_KEY had been set only in Preview.
She set it in Production and, remembering that variable changes affect only new deployments, redeployed. After confirming payment, booking and text at the new URL, she promoted that deployment. The domain moved without rebuilding. Finally, she refunded the test payment and reenabled automatic assignment. Her environment checklist changed from "Is the new variable in Production?" to "Is it in every environment where used, and was it checked at the production deployment URL?"
Ripple Swimming Pool: Saturday morning's 500s
Hyeonu, a developer operating the lesson registration app for "Ripple Swimming Pool," deployed a waiting-list registration feature at 9 a.m. on Saturday. At 9:12, an alert arrived: the number of 500 errors on the /api/lessons route had risen to dozens of times its normal level.
Hyeonu first checked the impact. Filtering runtime logs by status code 500 and the affected path showed that the same error had been recurring since 9:02. This coincided with the deployment time. The lesson list would not open at all, affecting most members. The deployment did not contain any changes to the database tables.
Before investigating the cause, Hyeonu used Instant Rollback in the dashboard to return to the immediately preceding working deployment, and the list opened again within one minute. Because he knew automatic assignment is disabled after rollback, he marked the "Rollback active" field in his checklist.
Only then did he investigate the cause. The error was Cannot read properties of undefined (reading 'length'), on the line where the new code read the waiting list for each lesson. The list was optional, but the new code assumed it was always present. Lessons created before the waiting-list registration feature did not have a list at all. The problem had not surfaced in the preview because its test data contained only newly created lessons.
On a feature branch, Hyeonu changed the code to treat an absent waiting list as an empty list, added test data that imitated old lessons and wrote a test. He opened both old and new lessons in the preview before merging. As expected, the production screen was still on the rolled-back version. Hyeonu promoted the new production deployment to end the rollback state, ran a smoke test and then cleared the mark on his checklist.
The incident record contained three lines: what happened (the code read a field absent from existing data), why the preview missed it (the test data contained only new records), and what would change (include samples in the older format in the preview data). The incident lasted 12 minutes, recovery took 1 minute, and the fix took an hour. If the order had been reversed, the outage would have lasted an hour.
Common mistakes
- "Preview worked, so production will too." Identical code seems enough, but environment variables, integrations and data differ. Preview success is necessary, but not sufficient.
- "Saving a changed environment variable applies it immediately." The settings screen suggests completion, but changes apply only to new deployments. Redeploy afterward.
- "Rollback restores data too." The name suggests everything returns, but rollback restores code and original deployment configuration only. Check compatibility first if the database structure changed.
- "Fix incidents directly in production." Finding the cause seems quick, but guessing prolongs disruption and rushed changes can cause another incident. Restore the previous good deployment first, then investigate on a branch.
- "After rollback, just push fixed code to
main." Normal pushes seem sufficient, but automatic domain assignment is disabled after rollback. Promote the fixed deployment to resume the usual flow. - "The build succeeded, so deployment succeeded." A green indicator reassures, but build success does not rule out runtime failures. Run a smoke test and inspect runtime logs immediately afterward.
Summary at a glance
| Topic | Remember |
|---|---|
| Deployment | A successful build result with a unique URL. Only the domain's target changes |
| Preview conditions | Non-production branch pushes, PRs, CLI without --prod. A new project's first deployment is always production |
| URLs | Branch URL means latest; commit URL means a fixed point |
| Protection | Standard Protection covers all except production domains. Use test keys and data for previews |
| Environment variables | Separate per environment. Changes affect new deployments only. Add new names everywhere required |
| Builds and logs | Build locally before deployment. Build failures: build logs; runtime errors: runtime logs |
| Before and after | Checklist records verified and unverified items. Smoke test immediately. Stage then promote |
| Rollback | Immediate without rebuild. Original settings, unchanged data, automatic assignment off → promote to return |
| Incident order | Assess → roll back → investigate → fix/preview → promote → record |
| Data changes | Expand–contract to keep rollback compatible at each stage |
Check the official docs
- Vercel: Deployments — the definition of a deployment, ways to create deployments, and promotion
- Vercel: Environments — conditions for preview and production deployments, the first-deployment rule, types of URL, and staged production deployments
- Vercel: Environment variables — per-environment variables, changes affecting new deployments only
- Vercel: Deployment Protection — scopes, methods and plan availability
- Vercel: Runtime Logs — log filters,
vercel logs, and the log retention period on each plan - Vercel: Instant Rollback — how rollback works, disabling and restoring automatic domain assignment, and the rollback range for each plan
- Vercel: Rolling Releases — releasing to a portion of traffic first
- Vercel: Usage & Pricing for Cron Jobs — the cron interval and timing precision on each plan
Self-check
5-1. When connecting a new project to Vercel for the first time, you run vercel from a feature branch without --prod. Is this first deployment a preview or production deployment? What rules apply to the deployments that follow?
5-2. A freelance designer wants to visit a work-in-progress page repeatedly for several days and always see the latest state. Which do you share, a branch URL or a commit URL? When would you use the other?
5-3. After rolling back to last week's deployment, you change an environment variable in project settings today. Does it apply to the currently serving rolled-back deployment? How do you apply it to production?
5-4. Which deployments are eligible for Instant Rollback on Hobby and on Pro?
5-5. To investigate an error from two hours ago, you run vercel logs --environment production in the terminal, but the records from that time do not appear. What is the command's default query range, and what should you add to see earlier records?
5-6. You want to check a deployment built with production environment variables yourself before it is assigned to the production domain. Which setting should you change in Vercel, and in what order should you proceed? When you promote the deployment after checking it, is it built again?
5-7. Write a six-line pre-deployment checklist for your app or a practice app. Include checks, preview flows, environment variables, data, external integrations and a recovery point, each using real feature names from your app.
Hands-on tasks
Exercise
Preview, environment variables, rollback and promotion (60 minutes)
- Connect a practice app to Vercel and create its first production deployment.
- Add a feature displaying a nonsecret environment variable such as
APP_LABEL. Set different values in Preview and Production. - Push a feature branch and confirm the Preview value at its URL. Merge into
mainand confirm the Production value live. - Change the Production value and compare the screen before and after redeployment.
- Use Instant Rollback to the previous deployment. Push a small
mainchange, confirm it does not automatically appear live, then promote the new deployment to resume normal operation.
Done when: you have a record of screenshots showing the value on the screen and the deployment status at every step, and the screenshots demonstrate both the difference in values before and after redeployment in step 4 and that automatic updates stopped in step 5 and resumed after promotion.
Exercise
Trace one incident through logs (40 minutes)
- In a practice app, create a route that returns a server error and writes a log only under a specific condition, such as
?fail=1. - Create a preview deployment and send several normal and failing requests.
- Filter runtime logs by 500 status and that path to find the first error time and message.
- Find the same details with CLI
vercel logs. Use--sinceif the default range misses them. - Write a three-line incident record: what, why, what changes.
Done when: the dashboard and CLI show the same first error time, and the three-line record is supported by actual log content.