# gh-ost Limitations You Need to Know Before Production

> The limitations and gotchas of gh-ost for online MySQL schema changes, and when to reach for something else instead.

Adela | 2025-08-29 | Source: https://www.bytebase.com/blog/gh-ost-limitations/

---

[gh-ost](https://github.com/github/gh-ost) is a solid tool for online schema changes in MySQL, but it's not a silver bullet. Some of its limitations are hard blockers that will stop a migration cold; others are quieter gotchas that only bite you at cut-over time or under heavy write load. This article walks through both, with the scenarios where each one tends to show up, so you know ahead of time whether gh-ost fits your case or whether you should reach for something else.

---

## 1. Hard Blockers: If You Hit These, Stop!

Some `gh-ost` limitations are non-negotiable. If your environment falls into any of the categories below, gh-ost simply won't run, and you'll need an alternative like [pt-online-schema-change](https://docs.percona.com/percona-toolkit/pt-online-schema-change.html) or a different migration strategy.

### Foreign Keys (FKs)

`gh-ost` does **not** support tables with foreign key constraints. If your table is referenced by other tables, meaning it's a "parent" table, `gh-ost` refuses to run. That refusal is deliberate: it's there to keep you from quietly breaking referential integrity. Helpful as a safety net, but a real wall for many applications.

Say you have a `customers` table with a primary key `id`, and an `orders` table with a foreign key `customer_id` that references `customers.id`. Try to alter `customers` with `gh-ost` and the migration fails, because `customers` is a parent table. Your options here are to temporarily drop the foreign key constraints, switch to a tool like [pt-online-schema-change](https://docs.percona.com/percona-toolkit/pt-online-schema-change.html), or do the migration inside a maintenance window.

### Triggers

Tables that already have triggers aren't supported either. If your table carries triggers for auditing, logging, or anything else, `gh-ost` can't migrate it.

So if your `payments` table has audit triggers, you'll need to move that auditing logic up into the application layer, or pick a tool that can work alongside triggers.

### No Shared Unique Key

`gh-ost` needs the source and ghost tables to share an identical `PRIMARY KEY` or `UNIQUE KEY` to iterate over the rows. If your schema change touches or removes that key, gh-ost can't proceed.

Changing the layout of a multi-column primary key is the classic example. You'll have to break the migration into steps (add a new unique key, backfill the data, then switch over to the new key) or use a tool that handles this kind of change directly.

## 2. Infrastructure Requirements: Must-Haves for a Smooth Migration

Past the hard blockers, `gh-ost` has a few infrastructure prerequisites that need to be in place before a migration goes smoothly.

### Binary Log Format and Image

`gh-ost` requires **Row-Based Replication (RBR)**. If your primary uses Statement-Based Replication (SBR), you'll need to point gh-ost at a replica configured with RBR. On top of that, many managed cloud database services require `binlog_row_image=FULL`.

**Example Command (for cloud environments):**
 ```bash
 gh-ost --assume-rbr --host=<replica_host> --cut-over=default ...
 ```

### Privileges

`gh-ost` needs specific replication-related privileges, and in some cases `SUPER`-like capabilities. Getting those can be awkward on managed platforms like [Amazon RDS](https://aws.amazon.com/rds/) or [Azure Database for MySQL](https://azure.microsoft.com/en-us/products/azure-database-for-mysql), where you don't have full control over the instance. Check your platform's docs for the permissions it grants to online schema migration tools before you commit to gh-ost.

## 3. The "Online" Lock: Cut-Over and Long-Running Transactions

"Online" doesn't mean zero locks. The final cut-over phase still needs a **metadata lock (MDL)** to swap the old and new tables. The lock is usually brief, but any long-running query against the table can block the swap, and a brief pause turns into a real incident.

Here's how it goes wrong: a business intelligence team kicks off a 15-minute `SELECT` on the table you're migrating, right as you're about to cut over. `gh-ost` waits for the MDL, your deployment hangs, and the lock can back up other application traffic behind it. The way around it is coordination: schedule cut-overs for low-traffic windows, give other teams a heads-up, and have a plan to kill any long-running query that gets in the way.

## 4. Performance and Capacity Pitfalls

`gh-ost` isn't a magic bullet for performance, and it comes with its own capacity considerations worth knowing up front.

### Headroom for a Full Copy

`gh-ost` creates a "ghost" table and copies every row from the original. That means you need enough free disk space for a full second copy of the table, plus the extra binary logs that pile up during the migration.

### High Write Rates Can Outrun gh-ost

Under extreme write loads, `gh-ost`'s single-threaded binary log processing can become the bottleneck. The backlog of changes to apply grows faster than gh-ost can drain it, and the migration falls further and further behind. [pt-online-schema-change](https://docs.percona.com/percona-toolkit/pt-online-schema-change.html) sometimes does better here. The honest answer is to test both in your own environment and see which one keeps up with your workload.

### Dropping the Old Table

After the cut-over, `gh-ost` drops the old table. On very large tables that drop can spike I/O and replication lag. Do it during a low-traffic window, or hand it off to a more careful housekeeping process for dropping big tables.

## 5. Topology Compatibility and Native DDL

### Galera/PXC Not Supported

`gh-ost` isn't compatible with [Galera](https://galeracluster.com/) or [Percona XtraDB Cluster (PXC)](https://www.percona.com/software/mysql-database/percona-xtradb-cluster), because of how it locks and swaps tables. For those topologies, [pt-online-schema-change](https://docs.percona.com/percona-toolkit/pt-online-schema-change.html) is the safer pick.

### Native MySQL May Be Faster

On modern MySQL (8.0+), a lot of DDL operations are now `INSTANT` or `INPLACE`, which means they run with near-zero impact and no external tool at all. Before reaching for [gh-ost](https://github.com/github/gh-ost), check whether a native DDL operation already covers what you need.

Adding a column with a default value, for instance, may be an `INSTANT` operation in MySQL 8.0, depending on the data type and default value. When that's the case, a plain `ALTER TABLE` is both simpler and faster than gh-ost.

---

## Quick Decision Checklist

A quick way to gauge whether `gh-ost` fits your migration:

- Table has **FKs or triggers**? → **Don't use gh-ost. Consider** [pt-online-schema-change](https://docs.percona.com/percona-toolkit/pt-online-schema-change.html).
- Can you get **ROW binlogs** and a **shared UNIQUE/PRIMARY key**? → **OK.**
- Can you coordinate the **cut-over** (no long queries)? → **OK.**
- Using **Galera/PXC topology**? → **Prefer** [pt-online-schema-change](https://docs.percona.com/percona-toolkit/pt-online-schema-change.html).
- On **MySQL 8.0+?** → **Check if native INSTANT/INPLACE DDL already solves it.**

If you can check every box and you've tested the migration on a replica first, `gh-ost` is an excellent choice for online schema changes on large, hot tables with minimal production impact.