Push to `build` to Test a Build Without Touching Your Running App

·4 min read·Updated
gitdeploymentci-cdworkflowai-agents

Git deploys here track two branch names, and most people only ever learn one. Pushing to deploy builds your image and replaces your running container — that's the deploy. Pushing to build builds the image and stops there, leaving whatever is currently running completely untouched. Since replacing a container means downtime — the old one is removed before the new one starts — and since most pushes while you're iterating are really asking "does this even build?", the second branch is the one you want far more often than you'd guess. Push to anything else and nothing happens, though at least it tells you so.

What each branch does

You push to What happens
deploy Image is built, then applied to your app — container replaced, brief downtime
build Image is built and stored. Your running app is not touched
anything else Nothing, plus an error message explaining why

That last case is explicit rather than silent. Push to main and the build output tells you:

You have just pushed to not tracked branch. Nothing will happen. Push to deploy or build branch to build and deploy your application.

Which is worth knowing, because "I pushed and nothing happened" is otherwise a confusing few minutes. The branch name is the destination, not your local branch — git push <remote> HEAD:deploy pushes your current branch to deploy regardless of what it's called locally.

Why build is the one you want while iterating

Think about what a normal debugging loop looks like. You change a dependency, fix a Dockerfile line, adjust a build step — and you want to know whether it compiles. You don't want your live app restarted for each attempt.

Restarting isn't free:

So the loop that respects your users is:

git push origin HEAD:build     # does it build?
git push origin HEAD:build     # and now?
git push origin HEAD:deploy    # ship it

Only the last one is visible to anyone using your app.

You get a real error when the Dockerfile is missing

A small thing that saves a genuinely baffling failure. Before building, the push checks that a Dockerfile exists at the root of what you pushed. If it doesn't, you get:

No Dockerfile found in the root of the pushed branch. Add a file named Dockerfile at the repository root and push again.

Without that check the build would fail much later with a raw Docker error about not locating the Dockerfile, delivered over the git protocol with no hint about the cause. Worth knowing what the message means: it's about the root of the pushed branch, which is exactly the trap that monorepos with backend/Dockerfile fall into.

Tell your coding agent about it

This matters more with an agent driving, because agents iterate quickly and don't feel the cost of a restart.

If you've connected an agent over MCP, it can fetch your deploy URL and push. Left to itself it will push to deploy every time, so a debugging session becomes a dozen container replacements on a live app. Say so explicitly:

Push to the build branch while iterating. Only push to deploy when I tell you the change is ready to go live.

Then check the build output rather than a summary — an agent reporting success has seen tool results, not a working app.

One push at a time

Pushes are processed one at a time per app, so two deploys landing together will race and one will fail. In CI that means a concurrency group; locally it means not pushing again while the previous build is still streaming output back.

One more CI-specific detail worth having: a push from a shallow clone is rejected. actions/checkout defaults to fetch-depth: 1, which produces a shallow history and a shallow update not allowed error. Set fetch-depth: 0 in the checkout step and it works — the full GitHub Actions setup.

How this compares to the zip path

If you deploy by uploading a zip, the agent-driven build has a daily allowance — the panel shows how many builds you have left today. That's a good reason to be deliberate about each upload.

Git pushes don't work that way, which makes build even more useful: you can validate as many times as you need, and spend a deploy only when there's something worth deploying. More on choosing a deploy path.

FAQ

Which branch do I push to for a git deploy?

deploy to build and go live, or build to build only. Other branch names are ignored with an explanatory message.

How do I test that my Dockerfile builds without deploying?

Push to the build branch. The image is built and your running container is left alone.

I pushed and nothing happened.

You pushed to an untracked branch. The output says so — push to deploy or build instead.

Why does my push fail with "shallow update not allowed"?

Your CI checkout is shallow. Set fetch-depth: 0 so the full history is available to push.

Can I push two changes at once?

No. Pushes are handled one at a time per app; use a concurrency group in CI so they queue instead of racing.

Why does the push complain about a missing Dockerfile?

It must be named Dockerfile and sit at the root of the branch you pushed — not in a subfolder.

Wrapping up

The default assumption is that a git-based platform has one magic branch and pushing to it ships. Here there are two, and the less famous one is the one that fits how people actually work: build repeatedly while you're figuring something out, deploy once when it's right. It costs nothing to adopt — change the ref you push to — and it takes your users out of your debugging loop entirely.