What happens when you deploy an app to Vercel?
Vercel takes a specific commit, installs your dependencies in a clean container, runs your build command, and turns the output into static files served from a CDN plus server functions for anything dynamic. If the build succeeds it becomes a deployment with its own URL, and your domain is pointed at it.
What are the steps of a deployment?
- 1A commit arrives, usually from a push to GitHub.
- 2A fresh container is created — nothing from your laptop comes with it.
- 3Dependencies are installed from your lockfile.
- 4Environment variables for the target environment are injected.
- 5Your build command runs; type errors and missing imports fail here.
- 6The output is split into static assets and server functions.
- 7Assets are distributed to the CDN; functions are deployed to run on request.
- 8The deployment gets a unique URL, and your domain is switched to it if it is a production build.
Why does the build fail there but not here?
- Your laptop still has a package installed that is no longer in the lockfile.
- A file name differs only by case — most Linux build machines care, macOS does not.
- The production build runs type checking that your dev server skips.
- An environment variable needed at build time is missing.
- The dev server tolerated an import that the production bundler will not.
A clean container is a feature. It means the deployment reflects your repository, not the accumulated state of your machine.
What is the difference between static files and server functions?
Static files — HTML, CSS, images, JavaScript bundles — are copied to servers around the world and returned instantly. Server functions run code per request: they can read secrets, query your database and return personalised responses. Anything that must stay private runs in a function.
| Runs | Can read secrets | Personalised per user |
|---|---|---|
| Static asset from the CDN | No | No |
| Server function | Yes | Yes |
| Browser JavaScript | No | Only with data it fetched |
Why is the deploy 'successful' but the page broken?
A successful build proves your code compiles, nothing more. Runtime failures — a missing production secret, a database rule that blocks real users, a callback URL still pointing at localhost — all pass the build happily and fail the first real visitor.
- 1Open the live URL in a private window so you are not signed in.
- 2Watch the Network tab while doing the broken action.
- 3Read the failing request's status and body.
- 4If it is a 500 from your own function, open the function logs in the hosting dashboard.
What about preview deployments?
Every branch or pull request can get its own deployment with its own URL. This is the cheapest safety net available: you can open the change as a stranger would before it touches your domain. Preview environments usually have their own environment variables, which is a common source of 'it worked in preview'.
Frequently asked questions
- Does deploying change my database?
- Not by itself. Code deployment and database migrations are separate steps, which is why new code can arrive before the table it expects.
- How do I roll back?
- Promote a previous deployment. Every build is kept with its own URL, so reverting is switching the pointer rather than rebuilding.
- Why is my change not live?
- Either the commit never reached the branch being deployed, or the deployment failed, or you are looking at a cached page. Check in that order.
Practice this in MessyDev
Reading it once helps. Doing it once sticks. These are the hands-on parts of MessyDev that cover the same ground.
Keep going
- Why isn't my GitHub deployment updating?Your change is committed and the live site is unchanged. Branch, build, cache or domain — how to find out which, in four checks.
- Why does my app work locally but not after deployment?The differences between your machine and a deployment — configuration, case sensitivity, build strictness, real users — and how to find which one bit you.
- Environment variables explained for beginnersWhat an environment variable is, why the same code behaves differently in two places, which ones reach the browser, and how to check whether yours is actually set.