How to Deploy an App With Claude Code
To deploy an app with Claude Code, you commit the project to a git repository, connect that repository to a host that builds automatically when you push, and then push. Claude Code's job is to write the deploy configuration, run the type check, make the commit, and push it. The host's job is to install dependencies, run the build command, and serve the result. Splitting the work that way is the whole trick, because the deployed site is then built from what is in the repository rather than from whatever happens to be sitting on your machine.
That distinction sounds like a technicality until it bites. I publish to seven production sites from one machine, every day, and every one of them ships by pushing a commit. The two times I broke something were both the same root cause: a build that came from my laptop instead of from the repository. Everything below is built around never letting that happen again.
- Get the project into a git repository with a branch you have agreed is the production branch.
- Connect the repository to a host that builds on push. Claude Code can write the config file the host expects.
- Put every generated file inside the build command so the host regenerates them instead of trusting yours.
- Push the commit. The host builds and deploys the pushed commit, not your working directory.
- Verify the live URL returns a 200 and serves the change you actually made, then note the rollback command.
Step One - Write the Deploy Contract Before Claude Code Touches Anything
The deploy contract is a five-line block you paste into your prompt or your project's CLAUDE.md file, and it is the single highest-leverage thing in this whole article. It removes every decision the agent would otherwise make for you at the worst possible moment. Without it, a deploy is the agent guessing which branch matters, which command builds the site, and whether it should force anything.
Here is the shape of it. Fill in the five values for your own project once and it stops being something you think about.
- Production branch: the exact branch name the host builds from. Never deploy from a detached head or a branch you cannot name.
- Build command: the one command the host runs. If it is not in the repository's config, the host does not know about it.
- Generated files: anything created at build time, such as a sitemap, an RSS feed, or a search index. These must be produced by the build command.
- Verification: the exact check that proves the deploy worked. A URL plus something specific you expect to find in the response.
- Rollback: the one command that puts the previous version back. Write it before you need it.
Step Two - Let the Host Build, Not Your Laptop
Deploy by pushing a commit and letting the host build it. Do not build locally and upload the finished output, even when the tooling makes that path look faster. A local build packages your working directory, which includes every uncommitted experiment you have open and, more importantly, excludes every commit someone else pushed while you were working.
This is the failure that cost me the most. On a site I share with a collaborator, I built locally and uploaded the output. My upload became the live site. His commits were still in the repository, perfectly safe, but they were not in my local tree, so they were not in what I uploaded. The live site rolled backwards and the repository said everything was fine. Nothing errored. There was no red text anywhere. The only symptom was a feature quietly missing from production.
Push-based deploys make that class of mistake structurally impossible, because the host builds the commit graph rather than your disk. If a collaborator's commit is in the branch, it is in the build. The rule I now hold to on every project is short: the thing that goes live is a commit, and the only way to make something go live is to push that commit.
Step Three - Pull Before You Push, Every Single Time
Fetch and rebase onto the production branch before you commit your own work. On any repository with more than one person pushing to it, the state you started your session with is already stale. Rebasing first means your commit lands on top of theirs instead of racing them.
The instruction to Claude Code is simple: fetch the remote, rebase onto the production branch, and only then stage and commit. If the rebase conflicts on files you did not write, stop and abort rather than resolving it blindly. An agent resolving a conflict in code it has never read is how someone else's work disappears.
There is a second-order effect worth knowing. If the collaborator's commit added a page or a route, and your build regenerates a sitemap, then you need to regenerate that sitemap after the rebase rather than before it. Otherwise you commit a sitemap that knows about your new page and has forgotten theirs.
Step Four - Put Every Generated File Inside the Build Command
Anything created by a script rather than typed by hand must be produced by the host's build command. A sitemap, an RSS feed, a generated types file, a search index: if the host does not regenerate it, the host ships whatever stale copy happens to be committed.
I lost a genuinely stupid amount of time to this on one site. The build command ran the bundler and nothing else. The sitemap generator was a separate script I ran by hand. So every deploy shipped a sitemap that was correct as of the last time I remembered to run the script, which meant new pages existed but were not listed anywhere a crawler would look. The pages were live. They were also invisible.
The fix is one line of configuration. Chain the generators into the build command so the host runs them on every deploy. Ask Claude Code to read the project's package configuration and move every generator into the build script, then push a commit and confirm the generated file changed in the deployed output. After that the file cannot go stale, because there is no longer a path where it is not regenerated.
Step Five - Verify the Deploy Instead of Trusting It
A deploy is not done when the agent says it is done. It is done when you have fetched the live URL and found the specific change you made. Those are different events and they can be minutes or hours apart, especially when a build fails silently or a cache serves an older copy.
Verification has two parts. First, the URL returns a 200 rather than a 404 or a redirect chain that ends somewhere unexpected. Second, the response body contains something that only exists in the new version. A status code alone proves nothing on a single-page app, because the host will happily return 200 for a route that does not exist and let the client render a not-found page.
- For a new page, check that the page URL returns 200 and that its title appears in the fetched HTML.
- For a code change, check that the newly built asset filename is being served, since most bundlers put a content hash in the filename.
- For anything with a sitemap, check that the new path appears in the live sitemap, not just the local one.
Tell Claude Code what the verification is as part of the request, not afterwards. The prompt that works: deploy this, then fetch the live URL and confirm the response contains the new heading, and tell me the exact status code you got. An agent asked to prove something behaves differently from an agent asked to do something.
The Two Failure Modes That Look Like Success
Both of the deploy failures worth memorizing share one property: nothing goes red. No error, no failed build, no alert. The deploy reports success and the site is wrong.
The two silent deploy failures and what actually fixes each
| What you see | What is really happening | The fix |
|---|---|---|
| Deploy succeeds, but a feature that was live yesterday is gone | You uploaded a locally built artifact that predates a collaborator's pushed commit | Deploy by pushing a commit so the host builds from the repository, never from your disk |
| New pages are live but nothing links to them and crawlers never find them | A generated file such as the sitemap is committed by hand and was never regenerated | Move every generator into the build command so the host regenerates it on every deploy |
The pattern underneath both is the same. A deploy is a promise that what is in the repository is what is on the internet. Any step that lets your local machine be the source of truth breaks that promise, and it breaks it quietly. That is why the deploy contract puts the branch and the build command first: they are the two places where the source of truth gets decided.
The Prompt I Actually Use
Here is the request, in the form that works reliably. Adapt the branch name and the verification to your project.
Deploy this project. First fetch the remote and rebase onto the production branch. Then run the type check and stop if it fails. Stage only the files I changed, by name, and do not stage anything else in the tree. Commit and push to the production branch so the host builds it. Do not build locally and do not upload a prebuilt output. When the build finishes, fetch the live URL, confirm it returns 200, confirm the response contains the new heading, and tell me the status code and what you found.
Every clause in that prompt closes a specific hole. Rebasing first stops the race. Stopping on a failed type check stops a broken build from becoming a deploy. Staging by name stops unrelated work in progress from being committed by accident. Refusing a local build stops the overwrite. Fetching the live URL turns a claim into a fact.
Where to Go From Here
Deploying is the step where a project stops being a folder on your machine and starts being something other people can use. Get the contract written once, and it becomes the least interesting part of your week, which is exactly what you want from it.
Inside Claude Code Club we run through this with real projects: members bring a build that works locally, we get it onto a host that deploys on push, and we write the deploy contract into their CLAUDE.md so the next deploy takes one sentence. If you want the version where someone looks at your actual repository instead of a generic tutorial, that is what the club is for.
Short, practical drops on skills, MCP, agents, prompts, and more. No spam, unsubscribe anytime.
Frequently asked questions
Can Claude Code deploy an app on its own?
It can run every command a deploy needs, including writing the host configuration, committing, and pushing. What it cannot do is decide which branch is production or what proves the deploy worked. Give it those two answers in a deploy contract and it handles the rest end to end.
Should I let Claude Code build locally and upload the output?
No. A local build ships your working directory, which excludes any commit a collaborator pushed while you were working. Push the commit and let the host build it, so the deployed version is always built from the repository.
Why did my new pages go live but never show up in search?
Almost always a generated file that the host never regenerated. If the sitemap is produced by a script you run by hand, the deployed sitemap is only as fresh as the last time you remembered to run it. Move the generator into the build command.
How do I verify a deploy actually shipped?
Fetch the live URL and look for something that only exists in the new version, such as a new heading in the HTML or a new content-hashed asset filename. A 200 status code alone is not proof, because single-page apps return 200 for routes that do not exist.
What is the safest way to roll back a bad deploy?
Redeploy the previous known-good commit rather than editing files on the host. Because the host builds from the repository, pointing it at an earlier commit restores a state you can reason about. Write that command down before your first deploy, not during your first outage.
Last reviewed by David Iya on August 14, 2026


