MySQL to Snowflake: The Complete Replication Guide

Intergalactic Data Labs
—
—
16 min read
As of September 2026 there are three ways to replicate MySQL to Snowflake. Snowflake's own Openflow connector reads the binlog from Snowflake's managed runtime. Hosted services like Fivetran, Airbyte, and Estuary do it from the vendor's cloud on a meter. Self-hosted CDC tools like Debezium, Sling, ingestr, and Filament run inside your network. Three questions decide. Must deletes propagate, may rows leave your network, and how often may the warehouse wake.
Approach | Change capture | Deletes | Where it runs | Pricing unit |
|---|---|---|---|---|
Snowflake Openflow | Binlog CDC | Soft-delete flag | Snowflake runtime or your AWS VPC | Snowflake credits |
Hosted ELT | Binlog CDC or query polling | Flag or hard delete | Vendor cloud | Rows, GB, or events |
Self-hosted CDC | Binlog CDC | Hard delete or flag | Your servers | Free or flat rate |
How does MySQL to Snowflake replication work?
Every approach does the same two jobs. It copies the tables as they stand into Snowflake, then it keeps Snowflake current by reading the changes MySQL writes to its binary log.
The binlog is the change source. The MySQL 8.4 manual calls the binary log "a record of the data changes to be sent to replicas." It is on by default, and the server source sets row format as the default, which every log-based tool needs. A replication tool connects as if it were a replica and applies each insert, update, and delete downstream.
Change data capture, or CDC, is the practice of reading that log instead of re-querying tables. The alternative is an incremental read on a timestamp column, which never sees a deleted row. Airbyte's MySQL docs make the case for the log, since binlog replication lets it "incrementally capture deletes in addition to new and updated records."
Snowflake loads files, not rows. Snowflake's fast path is COPY INTO, which loads staged Parquet, CSV, JSON, Avro, or ORC files from an internal stage or an external bucket. Every tool on this route stages a batch, runs COPY INTO, and applies the result with a MERGE, which Snowflake documents as useful "if the second table is a change log."
Passwords are on their way out. Snowflake's MFA rollout says it will "disallow passwords for all service users," with strong authentication required for everyone between August and October 2026. That means a key pair of at least 2048 bits on a SERVICE user, whichever tool you choose.
What decides which MySQL to Snowflake tool to use?
Six criteria settle the choice. Rank them by which one would hurt most if it went wrong.
Deletes. A query-based sync silently keeps rows MySQL deleted. Only binlog CDC sees them, and the tool then removes the row in Snowflake or flags it.
Consistent start. The snapshot and the change stream must share one point in the binlog, or rows land twice or never.
Warehouse wake-ups. Snowflake bills compute per second with a one minute minimum, so a tool that loads every few seconds costs more than one that batches.
Where the data runs. A hosted service routes rows through the vendor's cloud. A self-hosted tool keeps them inside your network.
Auth. The tool must support key-pair or token auth for the Snowflake service user.
Binlog retention. The log must outlive the snapshot and any pause. MySQL defaults to 30 days, but on Amazon RDS the default "means binary logs aren't retained," per the RDS docs.
Fivetran states the warehouse point directly in its Snowflake destination docs. "Because Snowflake bills for compute usage on a per-second basis, they charge you when Fivetran loads data." Its advice is an X-Small warehouse with one-minute auto-suspend, and that advice applies to every tool below.
Which tools replicate MySQL to Snowflake?
Twelve tools run this route. The table lists how each captures changes, what it does with deletes, where it runs, its license as stated in its LICENSE file, and its pricing unit.
Tool | Change capture | Deletes | Deployment | License | Pricing unit |
|---|---|---|---|---|---|
Filament | Binlog CDC | Merge removes | Binary, Go library, Helm | Apache 2.0 | None |
Snowflake Openflow | Binlog CDC | Flag | Snowflake runtime or AWS BYOC | Proprietary | Snowflake credits |
Snowflake Connector for MySQL | Binlog CDC | Flag | Native app plus Docker agent | Proprietary | Snowflake credits |
Fivetran | Binlog or Teleport | Flag | Hosted, Hybrid on Enterprise | Proprietary | Monthly active rows |
Airbyte | Binlog via Debezium | Flag, hidden in final table | Cloud or Helm | ELv2 | Credits per GB |
Estuary Flow | Binlog CDC | Hard or flag | Hosted, private, BYOC | BSL 1.1 | GB plus per connector |
Debezium plus Kafka connector | Binlog to Kafka | Append only | Kafka Connect | Apache 2.0 | Free, Snowpipe per GB |
dlt | Query based | Not from MySQL | Python library | Apache 2.0 | Free, dltHub paid |
Sling | Binlog CDC, paid tier | Hard or flag | CLI | GPL-3.0 | Flat rate token |
ingestr | Binlog CDC | Flag | CLI | FSL-1.1 | Free |
Hevo | Binlog CDC | Flag | Hosted | Proprietary | Events per month |
Stitch | Log based | Flag | Hosted | Proprietary | Rows per month |
Snowflake Openflow is the native answer and it is generally available. Built on Apache NiFi, it runs a snapshot and then applies CDC through MERGE on a replication key. Deletes set a _SNOWFLAKE_DELETED column rather than removing the row. The docs list real limits. It "supports only username and password authentication with MySQL," it cannot read Aurora reader instances, and BYOC is AWS only.
The Snowflake Connector for MySQL is the older native app. Its own page says it stays in preview, that general availability "is currently not on our product roadmap," and that Openflow replaces it. Treat it as a migration target, not a new install.
Fivetran has the broadest connector catalog and a mature MySQL source that pulls a full dump and then connects to the binary log. Its Teleport Sync captures changes with only a read-only SQL user when the binlog is unavailable. Deletes arrive as a _fivetran_deleted flag, since "we don't delete rows from the destination." The meter is monthly active rows, which grows with your data.
Airbyte ships a certified MySQL source with binlog CDC through Debezium and checkpointed, chunked reads. Its Snowflake destination writes "each stream into its own raw table" plus a typed final table. Every table exists twice, and the raw copy "is not deduplicated." The LICENSE is Elastic License 2.0 and cloud pricing bills database sources per GB.
Estuary Flow captures MySQL via the binary log, backfills first, then streams. Its Snowflake materialization applies changes transactionally and can hard delete rows when the option is on. It is hosted and priced per GB plus a monthly fee per connector, with private deployment on annual contract only.
Debezium is the reference binlog reader, but it has no Snowflake sink. Debezium Server lists Kafka, Kinesis, Pub/Sub, JDBC, and Iceberg, and Snowflake is absent. The path runs through Kafka to the Snowflake Connector for Kafka, which lands rows as VARIANT and does not reflect deleted messages. You write the MERGE yourself.
dlt, Sling, and ingestr round out the list. dlt's sql_database source reads MySQL by query, with no binlog CDC documented on that page. Sling reads the binlog but CDC requires a CLI Pro Max token. ingestr, a Go codebase at version 1.x, streams the binlog under the mysql+cdc scheme. Its LICENSE is the Functional Source License 1.1, not the MIT its docs index still claims.
Hevo and Stitch are hosted, log-based, and metered by events and rows. AWS DMS does not target Snowflake at all. Its target list stops at Redshift, S3, Kinesis, and Kafka.
Where does Filament fit for MySQL to Snowflake?
Filament is an Apache 2.0 replication engine written in Go, and Galaxy builds it. Measured against the six criteria above, it answers five with documented behavior and leaves warehouse wake-ups to the schedule you set.
Deletes and consistent start. The MySQL source bootstraps CDC from a consistent snapshot. For a table with no checkpoint, the stream "continues from the binlog position captured just before that snapshot opened." Table locks last "for the instant between capturing the position and opening the snapshot." The Snowflake sink offers cdc_merge, which applies each batch "with one Snowflake MERGE," so deleted MySQL rows are deleted in Snowflake.
Where the data runs. Filament runs as one binary, a Go module inside your service, or a Helm chart. The Snowflake sink uploads each Parquet batch "with PUT to a run-scoped path under the user's @~ stage." The same page adds that "no external storage integration is required." Rows go from your MySQL to your Snowflake stage and nowhere else.
Auth. The sink supports key-pair authentication and Snowflake programmatic access tokens. Its docs say "password authentication is not exposed," which matches where Snowflake is heading.
Integrity and restarts. The integrity page says that before every write "the pipeline calculates a CRC32-C checksum over the batch's Arrow buffers," and the sink recalculates it. Snapshots save the last key, so an interrupted copy resumes, and large tables split into as many as 64 primary-key ranges read in parallel. CDC runs stream from the checkpoint to the current position and exit, so continuous replication is a cron entry.
Maturity and speed deserve two plain sentences. The MySQL source is beta and the Snowflake sink is alpha today, per the connector overview. The public benchmark cohort has no MySQL to Snowflake run, so this route carries no speed claim.
How to replicate MySQL to Snowflake with Filament
What you need. MySQL 8.0 or later with InnoDB tables, a user holding REPLICATION SLAVE, REPLICATION CLIENT, SELECT, and LOCK TABLES, and binlog retention that covers the snapshot. Install the CLI with curl -fsSL https://getgalaxy.io/filament/install | sh or brew install galaxy-io/tap/filament, per the CLI page. The local CLI uses file-backed SQLite and needs no other service.
Prepare Snowflake. The sink page ships the full setup script. It creates a role, an X-Small warehouse with one minute auto-suspend, a database, and a SERVICE user with an RSA public key attached.
Generate the key with openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out snowflake_filament_snow_key.p8 -nocrypt and paste the public key body into the last line.
Declare the pipeline. The CLI reads ~/.config/filament/filament.yaml. The block below uses the field names from the MySQL source and Snowflake sink pages. The docs' own YAML example is Postgres to stdout, so confirm the nested auth keys against filament sink create --sink-connector snowflake --help, which lists every connector field.
Set MYSQL_DSN to a mysql:// URL and SNOWFLAKE_PRIVATE_KEY to the PEM contents, then run the pipeline.
What the first run produces. For each table, Filament creates a typed table in the target schema, mapping integers to NUMBER(38,0), timestamps to TIMESTAMP_NTZ(6), and JSON to VARIANT. It captures the binlog position, opens the snapshot, stages each batch as Parquet, loads it with COPY INTO, and removes the staged file. Then it applies the changes that landed during the copy. Rows carry _filament_ columns with run ID, operation, and position.
Keeping it current. Add a schedule to the pipeline, and each run catches up from the saved GTID or file position and exits. A run with no changes still advances the watermark. Delivery is at-least-once and merge converges by primary key, so a crash mid-transaction replays that transaction safely. Each COPY INTO wakes the warehouse, so a five-minute cron with the auto-suspend above keeps compute idle most of the day.
What breaks in a MySQL to Snowflake pipeline?
The rows rarely fail. The types do. These are the traps every tool on this route documents, with the fix for each.
Trap | MySQL behavior | Snowflake behavior | Fix |
|---|---|---|---|
Zero dates | Stores 0000-00-00 | No such value | Convert to NULL, not the epoch |
TINYINT(1) | Synonym for BOOL | BOOLEAN or NUMBER, tool decides | Choose per column before loading |
DECIMAL over 38 digits | Up to 65 digits | 38 digit maximum | Widen to text or round on purpose |
DATETIME | No time zone stored | TIMESTAMP_NTZ | Set the session zone once |
TIMESTAMP | Stored as UTC, shown in session zone | TIMESTAMP_TZ | Map to a zoned type |
ENUM and SET | Index plus label | TEXT | Expect the label, not the index |
JSON | Native JSON type | VARIANT | Load as VARIANT, not a string |
Zero dates are the loudest failure. MySQL converts invalid dates to the zero value when the SQL mode permits it. Airbyte converts those to NULL, Openflow maps them to the Unix epoch, and a NOT NULL destination column rejects either. Decide before the first load, because a rewrite after data lands means a re-snapshot.
Booleans and decimals need a decision, not a default. MySQL's BOOL is a synonym for TINYINT(1), and vendors disagree on the mapping. Airbyte emits boolean by default while Openflow emits INT. MySQL DECIMAL allows far more digits than Snowflake's NUMBER, and Openflow says "precision is lost when exceeded." Filament writes such columns as TEXT instead of rounding.
Lost logs and schema changes end the run. If the binlog is purged before the snapshot finishes, ingestr "fails instead of taking a partial snapshot," Fivetran requires a re-sync, and Filament re-snapshots the table. A column-count mismatch after DDL fails a Filament run rather than mis-mapping values, so an incompatible ALTER TABLE also means a re-snapshot of that table.
Which MySQL to Snowflake option fits which job?
Pick by the constraint that breaks first.
Job | Pick | Why |
|---|---|---|
Replica inside your network, deletes propagated | Filament | Snapshot plus binlog from one point, merge removes deletes, checksummed batches, no meter |
Everything inside Snowflake, no software to run | Openflow | Generally available, native MERGE, billed as Snowflake credits |
Widest catalog, budget by rows | Fivetran | Binlog or Teleport, user stage staging, MAR meter |
Hosted CDC with hard deletes | Estuary Flow | Transactional apply, hard delete option, per GB |
Kafka already in production | Debezium plus Kafka connector | The change stream is already there, MERGE is yours |
Code-first Python, deletes not required | dlt | Apache 2.0, many merge strategies, query-based reads |
For the main reader, a team that wants a current Snowflake copy of production MySQL without sending rows through a vendor, Filament is the pick. It answers deletes, consistent start, locality, auth, and integrity with documented behavior and runs as one process. A team that lives entirely inside Snowflake should take Openflow. A team that will not operate software should take a hosted service and set the auto-suspend low.
Related guides
Postgres to Snowflake, The Complete Replication Guide covers the same sink from the other database.
MySQL to Postgres, Migration and Replication Guide uses the same source connector.
Best Way to Sync Stripe to Snowflake (2026) walks through the Snowflake key-pair setup for a SaaS source.
What Is Change Data Capture (CDC)? The Complete Guide explains the log-reading model this route depends on.
Full Load vs Incremental vs CDC, Which to Use covers the read and write modes in depth.
Best CDC Tools in 2026 ranks the tool landscape.
Introducing Filament is the launch post.
Frequently asked questions
How do I replicate MySQL to Snowflake?
Snapshot each table, load it into Snowflake through a stage and COPY INTO, then read the MySQL binary log and apply inserts, updates, and deletes with MERGE. Snowflake's Openflow connector, hosted services like Fivetran and Estuary, and self-hosted tools like Debezium, Sling, ingestr, and Filament all follow this pattern.
Can MySQL replicate to Snowflake in real time?
Close to it. Log-based CDC reads the binlog as changes commit, but every tool then batches those changes into Snowflake, because each load wakes a warehouse that bills per second with a one minute minimum. Most teams run a catch-up every few minutes rather than streaming row by row, which keeps latency low and compute predictable.
Does MySQL to Snowflake CDC capture deletes?
Yes, if the tool reads the binary log. A timestamp-based incremental sync cannot see a hard delete because the row is gone. Tools differ in how they apply deletes. Fivetran and Openflow flag the row as deleted, Estuary and Sling can remove it or flag it, and Filament's merge mode removes it.
What binlog retention should I set for MySQL to Snowflake?
Long enough to cover the initial snapshot plus any pause in syncing. MySQL's own default is 30 days, but Amazon RDS retains nothing by default and caps retention at one week. Fivetran asks for at least a day, Estuary and Airbyte suggest a week or more. Set it before the first run, since a purged log forces a full re-snapshot.
Does the initial snapshot lock my MySQL tables?
Briefly, on most tools. Log-based replicators need one consistent point where the snapshot and the binlog position agree, and hosted MySQL services do not allow a global read lock, so tools take short table-level locks instead. Writers wait a moment while the position is captured, then the snapshot runs without blocking anyone.
What MySQL types break when loading into Snowflake?
Zero dates, which Snowflake rejects. TINYINT(1) columns that MySQL treats as booleans. DECIMAL columns beyond 38 digits, which Snowflake cannot hold. DATETIME columns with no time zone. ENUM and SET values, which land as text. JSON, which needs a VARIANT column rather than a string. Check each of these before the first load.
Does Snowflake still allow password login for replication tools?
Not for service users. Snowflake's own rollout says it will disallow passwords for all service users, and Fivetran, Airbyte, Estuary, and Filament all document key-pair authentication as the required or recommended path. Generate a 2048 bit RSA key pair, attach the public key to a SERVICE user, and hand the private key to the tool.
How do I set up MySQL to Snowflake replication with Filament?
Create a MySQL source with replication set to cdc, a Snowflake sink with key-pair auth, and a pipeline listing the tables with the merge write mode. The first run snapshots each table, stages Parquet to your own user stage, loads it with COPY INTO, and streams the binlog from the position captured before the snapshot. Later runs catch up from the saved checkpoint.
Does Filament need an S3 bucket to load Snowflake?
No. Its Snowflake sink encodes each batch as Parquet in memory, uploads it with PUT to a run-scoped path under the Snowflake user's internal stage, loads it with COPY INTO, and removes the staged object. No external storage integration is required, so no bucket, IAM role, or storage integration sits between MySQL and Snowflake.
Which tool is best for MySQL to Snowflake?
It depends on the constraint that breaks first. A team already inside Snowflake with no software to run should look at Openflow. A team that will pay per row for a managed service can take Fivetran or Estuary. A team that wants the replica to run in its own network, propagate deletes, and verify every batch can use Filament, with the Snowflake sink at alpha today.
More articles
Questions
Answered
FAQ
What does Galaxy do?
What is Filament?
What is enterprise context management?
What does working with Galaxy look like?
How do you handle security and compliance?
Why does Galaxy build in the open?
Company
Copyright © 2026 Galaxy. All rights reserved.