GitHub Actions Deploy vs the Host’s Git Integration: Two Ways the Same Push Ships
Jonah Hale
September 30, 2026
For about three weeks, every push to main on one of my side projects was deployed twice. I did not notice, because the second deploy was identical to the first and finished a couple of minutes later. The site stayed up. Nothing looked wrong.
Then a push with a failing test went live. The GitHub Actions run was red, with a big failed check on the commit, and yet the broken code was serving traffic. I stared at the workflow file for a while before I understood that the workflow had nothing to do with it. The deploy that shipped the broken commit came from somewhere else entirely.
The project is a small app for a local running club: race calendar, results uploads, a members page. I had connected the repository to the hosting platform on day one using the platform’s Git integration, which deploys automatically on every push. Months later, I asked a coding agent to “make sure tests pass before we deploy.” It did what I asked, sensibly: it wrote a GitHub Actions workflow that installed dependencies, ran the tests, and then deployed using the platform’s CLI with a token stored in repository secrets.
It did not turn off the Git integration, and I did not think to ask. So from then on, every push started two races. The platform’s integration saw the push and began building immediately. Actions saw the push, ran the tests, and if they passed, deployed again. When the tests failed, Actions stopped, correctly. The integration had already shipped.
That is the whole story, and it is a very common one. The question underneath it is simple: when you push, who ships it? There should be exactly one answer.
Two shippers, two philosophies
Both approaches are good. They just put the deploy decision in different places.
The host’s Git integration puts the decision on the platform. You connect a repository and a branch, and the platform watches for pushes. When one arrives, it clones the commit, builds it on the platform’s own build machines using environment variables stored on the platform, and deploys. Most integrations also build preview deployments for other branches and pull requests, and post the preview URL back to GitHub as a status.
A GitHub Actions deploy puts the decision in your repository. A workflow file describes the steps: check out, install, test, build, and then deploy by calling the platform’s CLI or API with a token. The platform just receives a finished build or an instruction to deploy a specific commit. Everything before that happens on GitHub’s runners, in a file you can read and version.
The Git integration is simpler to set up and harder to customise. Actions is more work to set up and can do almost anything. For an app a coding agent is actively changing, though, the differences that matter are not really about features. They are about gating, secrets, and who can change the rules.

Gating: does anything have to pass first?
This is where my double-deploy bit me, and it is the first thing I would check.
A plain Git integration deploys whatever you push to the production branch. It does not know or care whether your tests passed. Some platforms now let you require GitHub checks to succeed before a deployment is promoted to production, and some let you write a script that decides whether a build should run. If yours does, turn it on. If it does not, the integration will ship red commits as happily as green ones.
With Actions, gating is natural. The deploy step simply depends on the test step. If tests fail, the deploy job never starts. You can add other gates the same way: a type check, a lint, a bundle-size budget, a scan for secrets in the client build, a check that migrations have been approved. When an agent is committing, those gates are doing real work. They are the difference between “the agent pushed something” and “the agent pushed something that passed the checks I care about.”
The simplest way to protect production with either approach is branch protection. If the production branch only accepts merges from pull requests with passing checks, then even a naive Git integration only ever sees commits that passed. I should have had that from the start. It would have hidden my double deploy, but it would also have stopped the red commit from shipping.
Secrets: where does the deploy key live?
With the Git integration, the platform holds everything. Environment variables for the build and runtime are stored in the platform dashboard. GitHub never sees them. The only link between the two is an app installation that lets the platform read your repository.
With Actions, GitHub needs a deploy token for the platform, stored as a repository or environment secret. Depending on your setup, it may also need build-time environment variables. That means your deploy credential now lives in two places, and anyone who can change a workflow file can, in principle, use it.
That last point matters more with an agent than without one. A pull request that edits .github/workflows/deploy.yml can change what gets deployed, where, and with which token. GitHub has sensible protections: workflows triggered by pull requests from forks do not get secrets by default, and you can restrict secrets to specific environments with required reviewers. But in a private repository where the agent pushes branches directly, a workflow change on a branch can run with access to secrets unless you have set those restrictions.
What I do now: the deploy token lives in a GitHub environment called production, which only the main branch can use, and which requires my approval for any job that references it. Workflow files are listed in CODEOWNERS so that changes to them need my review. And the agent’s instructions say plainly that it may propose changes to workflows but should call them out at the top of the pull request description.
Who can change the rules
This is the subtle difference between the two approaches, and I think it is the most important one for agent-driven projects.
With the Git integration, the deploy rules live in the platform’s dashboard. Which branch is production, what the build command is, which variables exist. An agent working in the repository usually cannot change them. If you want to change how deploys work, you log into the dashboard.
With Actions, the deploy rules live in the repository, as code. That is great for reviewability, since every change is a diff, and it means you can roll back a bad pipeline change like any other commit. But it also means the thing being deployed can edit the thing that deploys it. An agent asked to “fix the failing build” might, quite reasonably, decide that the fastest fix is to mark a flaky test step as continue-on-error. Now your gate is gone, and the change looks like a small YAML tweak in a larger diff.
I have seen exactly that happen, not on my project but on a friend’s. The agent was not being sneaky. It was being helpful in the most direct way it could find.

Other differences worth knowing
Previews. Git integrations are usually excellent at preview deployments for pull requests, with URLs posted back to GitHub automatically. Recreating that in Actions is possible but fiddly. Many people keep the integration for previews and use Actions only for production, which is a valid split as long as the integration is explicitly told not to deploy the production branch.
Build environment. With the integration, your code builds on the platform’s machines, which match its runtime. With Actions, it builds on GitHub’s runners, which may not. Native dependencies and build caches occasionally behave differently.
Extra steps. If a deploy needs to run a migration, warm a cache, upload source maps, notify a chat channel, or tag a release, Actions does it naturally. Integrations usually offer a build command and not much else.
Cost and time. Actions runs cost minutes on private repositories beyond the free allowance, and they add a few minutes of latency before the deploy starts. For a small project, neither is significant. The integration’s builds are usually included in the platform’s plan.
Visibility. With Actions, the full deploy log sits next to the commit in GitHub. With the integration, it lives in the platform dashboard, and GitHub shows a status link. When an agent is reading logs to debug a failed deploy, it can usually read the Actions logs directly; platform logs may need a CLI and a token.
How I choose now
For the running club app, I kept both, with a clear division of labour. The platform integration builds previews for pull requests only; production deploys from it are disabled in the dashboard. A single Actions workflow owns production: it runs on pushes to main, runs tests, and deploys with a token scoped to a protected environment. Branch protection on main requires the test check to pass before anything merges.
If I were starting a new project today, here is how I would decide.
- Use the host’s Git integration alone if the app is simple, you have branch protection with required checks, and you want the fewest moving parts. Especially if the platform can wait for GitHub checks before promoting.
- Use GitHub Actions for production if deploys need extra steps, you want gates beyond “tests passed,” or you want the whole pipeline versioned and reviewable. Protect the token with environments, and protect workflow files with code owners.
- Use both only with a written division: previews from the integration, production from Actions, and the integration’s production deploys switched off.
Whichever you pick, do one check today: push a trivial commit to your production branch and count how many deploys start. If the answer is more than one, you have two shippers, and sooner or later one of them will ship something the other would have stopped.
It took me three weeks and a red commit on the live site to count mine. It takes thirty seconds to count yours.