Skip to main content
PostgreSQL → MySQL

Sync PostgreSQL to MySQL
with real-time CDC.

Stream every INSERT, UPDATE, and DELETE from PostgreSQL to MySQL in sub-second latency — via logical replication, not polling. Automatic type mapping, PII masking, resumable.

TL;DR

rsync.ai subscribes to PostgreSQL logical replication (pgoutput plugin) and writes change events to MySQL as they happen. Takes an initial consistent snapshot first, then switches to streaming. Handles type mapping automatically: UUID→CHAR(36), JSONB→JSON, TIMESTAMPTZ→DATETIME. Works with self-hosted Postgres, RDS, Aurora, Cloud SQL, Supabase, and Neon as source; MySQL 5.7+, RDS, Aurora, Cloud SQL, Azure as destination.

  • Real-time CDC via logical replication — sub-second latency, no polling
  • Automatic type mapping: UUID, JSONB, TIMESTAMPTZ, arrays
  • Initial snapshot + live streaming — consistent from day one
  • Self-hosted, source-available under Elastic License 2.0
How it works

How to sync PostgreSQL to MySQL — 5 steps

From enabling logical replication to rows streaming into MySQL.

  1. 1

    Enable logical replication on PostgreSQL

    Set `wal_level=logical` in postgresql.conf (or via the RDS parameter group). Set `max_replication_slots` to at least 2 and `max_wal_senders` to at least 2. Then create a replication slot and a publication: `SELECT pg_create_logical_replication_slot('rsync_slot', 'pgoutput');` and `CREATE PUBLICATION rsync_pub FOR ALL TABLES;`. Restart Postgres if you changed wal_level.

    wal_level=logical · pgoutput plugin
  2. 2

    Connect PostgreSQL in rsync.ai

    Provide host, port (default 5432), user, password, database name, the replication slot name (`rsync_slot`), and the publication name (`rsync_pub`). The user needs REPLICATION privilege: `ALTER ROLE your_user REPLICATION LOGIN;`. rsync.ai supports self-hosted Postgres 10+, Amazon RDS, Aurora PostgreSQL, Google Cloud SQL, Supabase, and Neon.

    host · port · user · password · database · slot · publication
  3. 3

    Connect MySQL as the destination

    Provide host, port (default 3306), user, and password. The user needs CREATE, ALTER, INSERT, UPDATE, DELETE on the target database. rsync.ai supports MySQL 5.7+, MySQL 8.0+, MariaDB 10.3+, Amazon RDS for MySQL, Amazon Aurora MySQL, Google Cloud SQL for MySQL, and Azure Database for MySQL. SSL and SSH tunnel are both supported.

    mysql://user:pass@host:3306/db — MySQL 5.7+ / RDS / Aurora / Cloud SQL
  4. 4

    Describe the sync in plain English

    Type what you want: 'Replicate all tables from the public schema of my Postgres database to MySQL in real time.' rsync.ai reads the publication's table list from Postgres and proposes a target MySQL database with the same table names. You can rename tables or restrict to a subset at this step.

    No SQL · No YAML · No DAGs
  5. 5

    Approve DDL and start streaming

    rsync.ai shows the proposed MySQL CREATE TABLE statements with automatic type mappings applied — UUID→CHAR(36), JSONB→JSON, TIMESTAMPTZ→DATETIME, arrays→JSON. Review and adjust any column. Approve, and rsync.ai takes an initial snapshot then switches to streaming CDC. Each change (INSERT/UPDATE/DELETE) arrives in MySQL in sub-second latency.

    Initial snapshot → live CDC stream · sub-second latency

PostgreSQL → MySQL type mapping

Default mapping rsync.ai proposes. Override any column type before approving.

PostgreSQL column (type)MySQL column (type)Notes
users (id UUID, email TEXT, created_at TIMESTAMPTZ)users (id CHAR(36), email TEXT, created_at DATETIME)UUID→CHAR(36), TIMESTAMPTZ→DATETIME
orders (id UUID, user_id UUID, total NUMERIC(10,2), status TEXT)orders (id CHAR(36), user_id CHAR(36), total DECIMAL(10,2), status VARCHAR(255))NUMERIC→DECIMAL, TEXT→VARCHAR(255)
products (id BIGSERIAL, metadata JSONB, tags TEXT[])products (id BIGINT, metadata JSON, tags JSON)JSONB→JSON, TEXT[]→JSON
events (id BIGSERIAL, payload JSONB, recorded_at TIMESTAMPTZ)events (id BIGINT, payload JSON, recorded_at DATETIME)JSONB→JSON, TIMESTAMPTZ→DATETIME
sessions (id UUID, user_id UUID, expires_at TIMESTAMPTZ)sessions (id CHAR(36), user_id CHAR(36), expires_at DATETIME)UUID→CHAR(36), TIMESTAMPTZ→DATETIME
audit_logs (id BIGSERIAL, row_data JSONB, changed_at TIMESTAMPTZ)audit_logs (id BIGINT, row_data JSON, changed_at DATETIME)JSONB→JSON — high-volume table, consider partitioning in MySQL

rsync.ai vs. Fivetran, Airbyte, AWS DMS for PostgreSQL → MySQL

What you give up — and gain — choosing rsync.ai for Postgres to MySQL replication.

Feature
rsync.aiyou
Fivetran
Airbyte
AWS DMS
Real-time CDC (sub-second latency)
Plain-English pipeline setup
Automatic UUID / JSONB type mapping
Self-hosted (data stays in your network)
Source-available connector code (auditable)
No per-row / per-MAR pricing
Resume after downtime (no data loss)
PII column masking built-in

PostgreSQL to MySQL — frequently asked

What is the CDC latency from PostgreSQL to MySQL?

Sub-second in normal operation. rsync.ai reads change events from the PostgreSQL WAL via logical replication, transforms the row, and writes to MySQL typically within 100–500ms of the original commit — limited by network round-trip between Postgres and rsync.ai, and rsync.ai and MySQL. Batching is configurable: lower batch size reduces latency, higher batch size improves throughput.

How are PostgreSQL types mapped to MySQL types — especially UUID and JSONB?

rsync.ai applies a deterministic type mapping: UUID→CHAR(36), JSONB→JSON, JSON→JSON, TEXT[]→JSON, INTEGER[]→JSON, TIMESTAMPTZ→DATETIME (UTC, precision 6), TIMESTAMP→DATETIME, DATE→DATE, BOOLEAN→TINYINT(1), NUMERIC(p,s)→DECIMAL(p,s), BYTEA→LONGBLOB, and TEXT→LONGTEXT (or VARCHAR(255) for short columns). You can override any mapping in the approve step before the pipeline starts.

What happens with MySQL reserved keywords in PostgreSQL column names?

rsync.ai detects MySQL reserved keywords in the proposed DDL (e.g., `order`, `rank`, `key`, `desc`) and automatically backtick-quotes them in the CREATE TABLE and all subsequent DML. You'll see a warning in the approve step listing any renamed or quoted identifiers so there are no surprises.

Do tables need a primary key in PostgreSQL for CDC to work?

For UPDATE and DELETE events, PostgreSQL logical replication requires either a primary key or REPLICA IDENTITY FULL set on the table. Without one, Postgres cannot identify which row changed. rsync.ai warns you if it detects tables in the publication that lack a primary key or REPLICA IDENTITY FULL. INSERT-only tables work fine without a primary key.

What happens if rsync.ai is down for several hours — are changes lost?

No. The PostgreSQL replication slot retains all WAL segments until rsync.ai confirms it has consumed them. When rsync.ai restarts, it resumes from the last committed LSN. The MySQL destination is written idempotently (upsert on primary key for INSERTs and UPDATEs), so if rsync.ai replays events after a restart, no duplicate rows appear. Monitor WAL disk usage on Postgres if downtime is extended — set `max_slot_wal_keep_size` (Postgres 13+) to guard against disk exhaustion.

How does rsync.ai handle schema changes (ALTER TABLE) in PostgreSQL?

DDL events (ALTER TABLE ADD COLUMN, DROP COLUMN, etc.) are not part of the pgoutput logical replication stream. rsync.ai detects schema drift by comparing the replication message column list against the known schema on each batch. When a new column appears, rsync.ai pauses, proposes the corresponding MySQL ALTER TABLE, and waits for your approval before resuming. Dropping a column in Postgres causes rsync.ai to emit a warning and stop writing that column to MySQL.

Can rsync.ai mask PII columns before they reach MySQL?

Yes. In the pipeline configuration, mark any column as `masked` — rsync.ai replaces the value with a SHA-256 hash or NULL before writing to MySQL. Masking happens inside the rsync.ai pipeline process: the raw value is never written to MySQL. Common use cases: email, phone, SSN in users or orders tables.

Is rsync.ai self-hosted for PostgreSQL to MySQL replication?

Yes. The full rsync.ai stack runs on your infrastructure via `docker compose up`. Your PostgreSQL replication credentials and MySQL password never leave your network. Everything runs inside your VPC or on-premise environment. License is Elastic License 2.0 — free to self-host, cannot be resold as a managed service.