← Back to blog

Replace dbt Cloud with a Free GitHub Actions Runner

Why we moved our dbt orchestration from dbt Cloud (now Fivetran Transformations) to GitHub Actions — and why your CTO should consider doing the same. Free, in-code, self-contained, and no separate platform to onboard.

Kevin McLaughlin
8 min read

If you run dbt in production, you almost certainly run it on dbt Cloud — which, as of late 2025, is part of Fivetran. It's the obvious choice. It's the official option. It has a UI.

It's also unnecessary for most teams. We moved our dbt orchestration to GitHub Actions and got back something better: a runner that's free, triggered by every PR merge, posts results to Slack, and is fully orchestrate-able by API. Everything lives in the same GitHub repo as the dbt project itself. There's no second platform to onboard to. There's no second bill to pay.

Here's the case for doing the same.


What we were running into on dbt Cloud

dbt Cloud has a free tier. It's generous on paper — 1 developer seat, 3,000 model runs/month, scheduled jobs, the IDE, the lineage browser. For a small team running a nightly build that touches 50 models, you'll fit comfortably.

The trouble starts when any of these become true:

  • You want to run dbt more than once a day. A nightly build covers reporting, but it doesn't cover operational use cases — a sales rep looking at a customer timeline at 2pm doesn't want last night's data. We were already burning through monthly model runs trying to keep our data fresher than 24 hours.
  • You want to trigger a run from your own app. That requires the dbt Cloud API, which isn't in the free tier. Same for webhooks.
  • You want failure notifications in Slack. Webhooks again. Not free.

Once you cross any of those lines, you're on a Team or Enterprise plan, and you're paying per developer seat for what is, at the end of the day, a cron job. With Fivetran's acquisition consolidating the spend, the platform pitch is getting heavier ("transformations as part of the platform") while the thing most teams actually need from dbt Cloud is orchestration. A way to run dbt on a schedule, on push, and on demand, with a notification when it breaks.

GitHub Actions does that for free.


What we replaced it with

A single workflow file in the same GitHub repo as the dbt project. It does four things:

  1. Runs dbt build on every push to master. Merge a PR, models rebuild.
  2. Runs dbt build on a cron. Default nightly. Could be every 15 minutes.
  3. Accepts manual and API-triggered runs. A POST to GitHub's workflow_dispatch endpoint kicks off a run with whatever target and command you want.
  4. Posts to Slack on success and failure. With a link back to the run logs.

That's it. ~150 lines of YAML. Sits in .github/workflows/dbt.yml. Reads credentials from GitHub Secrets. Done.

For the actual code, see the step-by-step setup doc. What follows is the case for why this is the right call for most teams.


Why this is better, not just cheaper

The cost story is real — for a private repo on the Team plan, you'd struggle to spend $5/month on a project that runs hourly. But cheaper isn't the most interesting part. The architecture is.

Everything is in code, in one repo

The biggest hidden cost of dbt Cloud isn't the bill — it's that your production data pipeline now spans two systems. The models live in GitHub. The schedule lives in dbt Cloud. The notifications live in dbt Cloud. The job configurations live in dbt Cloud. The list of which environment variables are set lives in dbt Cloud.

When something breaks at 9pm on a Friday, you have to know that.

When a new analyst joins, you have to onboard them onto two platforms. You have to grant access on two platforms. When they leave, you have to revoke access on two platforms.

With GitHub Actions, access to the GitHub repo is access to everything. The workflow definition is a file. The schedule is a cron line in that file. The Slack target is a secret in repo settings. The history of every run is in the Actions tab. There's no second console to learn, no second SSO integration to maintain, no second access matrix to keep in sync.

For a CTO trying to keep the operational surface area small, this matters more than the savings.

Push triggers are the right default

dbt Cloud supports CI runs on PRs. It does not, by default, run prod on merge — you set up a separate scheduled job for that. The result is that your prod data lags your prod code by up to 24 hours.

GitHub Actions inverts this. The default is "merge → run." Code and data move together. The same merge that ships a model change rebuilds the affected models in prod, and you find out within minutes (via Slack) whether the change broke something. Most data engineering teams say they want this. Few have it set up.

API triggers unlock orchestration

This is the one that surprised us most. Once dbt is behind a workflow_dispatch endpoint, anything that can speak HTTP can become a dbt orchestrator.

We trigger dbt runs from our internal app's serverless backend (Convex) when certain ingestion jobs complete. Other teams could trigger from a Lambda, an Airflow DAG, a webhook from Fivetran's ingestion side, or a Slack slash command. The runner doesn't care what triggered it — push, schedule, or API are all the same shape.

dbt Cloud has an API for this. It's a paid feature.

The free runner means you can run dbt more often

Once orchestration is free, "how often should we run dbt?" stops being a budget question and becomes a freshness question. We moved one client from nightly to every 4 hours because their support team needed same-day data. That would have meant a meaningful upgrade on dbt Cloud. On GitHub Actions it's a one-line cron change.

Slack notifications come included

The workflow posts a Slack message on every success and every failure with a link back to the run. We get our failure observability — the thing that actually matters — from a free incoming webhook URL. dbt Cloud charges for this on paid tiers via webhook integrations.


What you give up

Three things, honestly:

The IDE. dbt Cloud has a browser-based IDE. GitHub Actions doesn't. Most analytics engineers we work with use VS Code or Cursor anyway, but if your team relies on the dbt Cloud IDE for analyst-led model work, that's a real trade-off. (Counter-argument: you can hand an analyst a GitHub Codespace and they get an in-browser editor with the project pre-cloned.)

The lineage browser. dbt Cloud has a nice DAG viewer. The dbt CLI ships with dbt docs generate which produces the same lineage as a static site. You can host it on GitHub Pages or Cloudflare Pages from the same workflow. We do.

The marketing surface. When a vendor asks "what's your data stack?" and you say "GitHub Actions" instead of "dbt Cloud," you'll get a few raised eyebrows. This evaporates the moment you describe what it actually does.

That's the list. We haven't missed anything else.


Who shouldn't do this

A few cases where dbt Cloud is the right call:

  • Large analyst teams writing models in the IDE. If you have 20 analysts developing models in dbt Cloud's browser IDE, that's a real workflow built on top of dbt Cloud. Replacing it is more work than replacing the runner.
  • Teams already deeply on Fivetran for ingestion. The integrated story ("ingestion + transformations on one bill") has real value if you're already paying that bill. Less compelling for teams using Airbyte, Stitch, or custom pipelines.
  • Teams with strict UI-driven runbook requirements. If your on-call needs to retry jobs from a managed UI rather than the GitHub Actions tab, dbt Cloud's job interface is more polished. (Though "Re-run failed" works fine in GitHub.)

For everyone else — and that's most teams running dbt — the GitHub Actions runner is the right default.


The setup

The full step-by-step is in Running dbt on GitHub Actions. Short version:

  1. Drop a dbt.yml workflow in .github/workflows/.
  2. Drop a profiles/profiles.yml in the repo root with environment-variable credentials.
  3. Add BIGQUERY_KEYFILE (or SNOWFLAKE_*) and SLACK_WEBHOOK_URL as repo secrets.
  4. Merge to master.

You're done. The first push triggers a build. Slack pings you when it finishes. The next morning's cron fires. If your app has the GitHub token, you can POST to a URL and trigger a run from anywhere.

The whole thing is one file you can copy from the docs into your repo. No separate platform. No separate bill. No second console to keep your team's access in sync with.


Closing thought

dbt Cloud was the right answer when GitHub Actions had stricter limits and fewer integrations. That changed. The Fivetran acquisition is a reasonable moment to ask whether you actually need both halves of what dbt Cloud bundles, and most teams don't. They need the runner, not the platform.

Move the runner into your repo. You'll move faster, pay less, and have one fewer system to onboard people onto.


For the technical setup, see Running dbt on GitHub Actions. For more on dbt-nexus and how we structure data warehouses for operational use, see Data Beyond Dashboards.