When you push to a branch a project is mapped to, Kylth applies the project’s pending Supabase migrations. This page is what happens in between. Setting the connection up is on the GitHub integration page.
1. The push arrives
GitHub sends a signed push webhook. Kylth acts on it when all of these hold:
- the push is to a branch, not a tag, and isn’t a branch deletion
- some project is mapped to that repository and that branch
- the push touches at least one file under
supabase/migrations/
Otherwise it’s acknowledged and ignored. A repeated delivery of the same commit is ignored too, so GitHub’s retries can’t apply anything twice.
2. Kylth lists the migrations at that commit
It reads supabase/migrations/ at the pushed commit, taking *.sql files directly in that folder. Subfolders are ignored.
Each file’s name is <version>_<name>.sql, the same convention the Supabase CLI writes, for example 20260912072300_add_orders.sql. The version is everything before the first underscore, and files without one are skipped. They’re ordered by version.
3. It compares them with the database
Kylth queries the project’s own supabase_migrations.schema_migrations table each run, so it always works from what’s really applied. Running supabase migration up by hand against the same database doesn’t put Kylth out of step.
Anything present in the repository and missing from that table is pending.
If the two histories disagree, nothing is applied
The deployment stops as diverged, before touching the database, when:
- A version the database has applied isn’t in the repository at this commit. A migration was renamed, deleted or rewritten.
- A pending version sorts below the highest applied one. Applying it would mean running an older migration after a newer one.
Kylth asks you to fix the repository, or correct schema_migrations, and push again. It doesn’t guess.
4. It waits for you, unless told not to
By default, a deployment waits as awaiting approval, and the project shows Approve and apply and Reject.
- Per project: always auto-apply, always require approval, or follow the instance default.
- Instance default: Settings → Migration deployments → Default policy. A new installation starts at “require approval”.
If another push lands on that branch while a run is waiting, the waiting one is marked superseded, naming the commit that replaced it. Approving a stale diff is never possible.
5. Before it writes anything
The project must be ready:
- it must be running, because migrations are applied inside its live database container
- no backup, restore, update or template refresh may be in progress
- at least 10 GB free on the projects volume
Deployments for one project never overlap: a second run waits for the first, for up to an hour.
Projects on a second host can’t deploy migrations yet. Kylth refuses, naming the host, and nothing is applied.
Production projects are backed up first
If the project’s environment is exactly Production, Kylth takes and verifies a full backup before applying anything. That happens even when the project is set to auto-apply; it only skips the human, not the backup. If the backup fails, no migration is applied.
6. It applies them, one file at a time
Each file runs through psql inside the project’s database container, as a single transaction that stops at the first error. The file’s own row in schema_migrations is written inside that transaction, so a file either applies completely and is recorded, or leaves nothing behind. Each file gets up to 15 minutes.
Files are applied in version order, and the run stops at the first failure.
A run isn’t a transaction. If the third file fails, the first two stay applied, exactly as the Supabase CLI behaves: migrations are forward-only and there’s nothing to roll back to. The failure message names the file that failed and the versions that survived, and for a production project it names the backup taken beforehand.
Watching a deployment
The project’s Migration deployments card lists each run, newest first, with its commit and status:
| Status | Meaning |
|---|---|
| Awaiting approval | Waiting for you to approve or reject |
| Pending | Queued to run |
| Running | Applying now |
| Completed | Every pending migration applied |
| Failed | Stopped at a file; the message says which, and what stayed applied |
| Diverged | The histories disagreed; nothing was applied |
| Rejected | You rejected it |
| Superseded | A newer push replaced it before it ran |
Edge Functions
The same push also updates Edge Functions, as a separate job: the contents of supabase/functions/ at that commit replace the project’s functions, and the functions container is restarted. Functions the repository no longer contains are removed, since the repository is the source of truth.