Skip to main content
Lifetime license included with every purchase
n8n workflowsMySQL syncdatabase automationdata sync

n8n MySQL Sync: Build a Two-Way Database Workflow

Build an n8n MySQL sync that upserts rows, dedupes, and runs both ways on a schedule. Exact node config, the upsert trick, and the parse steps tutorials skip.

Nn8n Marketplace Team·October 27, 2026·Updated October 27, 2026·8 min read

A CRM holds the live contact record. MySQL holds the one the analytics team queries. Both drift apart within a day because nobody reconciles them by hand. n8n MySQL sync closes that gap: the workflow reads what changed, decides which side wins, and writes the difference, on a schedule you set once.

The tutorials that rank for this query describe the idea and stop. They show an Insert node and call it a sync. A real sync needs a dedupe step, an upsert so re-runs don't duplicate rows, and a way to handle the same record changing on both sides. This post covers those, with the exact MySQL node config and the SQL behavior that makes re-runs safe.

Syncing to a database is the part of automation people underestimate. The happy path is five nodes. The version that survives a second run is the one worth building.

What you can sync with the MySQL node

The MySQL node (n8n-nodes-base.mySql) runs Select, Insert, Update, Delete, and raw queries against any reachable MySQL or MariaDB instance. That covers the sync patterns most teams actually need:

  • Mirror new CRM contacts into a reporting database the moment they're created
  • Push form submissions into a MySQL table and back out to a Sheet
  • Keep a product catalog in MySQL aligned with a Shopify or Airtable source
  • Pull MySQL rows on a schedule and fan them out to Slack, email, or a dashboard
  • Reconcile two systems nightly so analytics never queries stale data
  • Archive old rows to a cold table and delete them from the hot one

The first three are sync jobs. The last three are read-and-route. Both lean on the same node; what differs is whether you write back.

The MySQL sync pipeline

Schedule Trigger (every 10 min)
   → Select changed rows (MySQL: WHERE updated_at > last_run)
   → Compare against target keys (Code / IF)
   → Upsert (MySQL Insert with ON DUPLICATE KEY UPDATE)
   → Log result + alert on failure

The load-bearing piece is the WHERE updated_at filter. Pulling the entire table every run and diffing in n8n works for a few hundred rows and falls over at a few hundred thousand. Let MySQL do the filtering. It's built for exactly that.

1. Read only what changed

Open with a Schedule Trigger (n8n-nodes-base.scheduleTrigger v1.2) and a MySQL Select that filters on a timestamp:

SELECT id, name, email, updated_at
FROM contacts
WHERE updated_at > '{{ $json.lastRun }}'
ORDER BY updated_at ASC

Store lastRun in n8n's static workflow data or a small state table so each execution picks up where the last left off. Pulling the full table every 10 minutes is the mistake that turns a tidy sync into a slow one.

2. Compare against the target

You've got the changed source rows. Now check what already exists on the target side. For small batches, an IF node (n8n-nodes-base.if v2) comparing a key field is enough. For larger sets, run a single MySQL Select that returns existing keys and match them in a Code node (n8n-nodes-base.code v2, jsCode) instead of querying once per row.

One query for 500 keys beats 500 queries for one key each. The execution log makes the difference obvious: a per-row loop shows 500 MySQL calls; the batched version shows one.

3. Upsert, don't blind-insert

This is what separates a sync from a duplicate factory. If your target table has a unique key on, say, email, the MySQL node's Insert operation can ride MySQL's ON DUPLICATE KEY UPDATE:

INSERT INTO contacts (email, name, updated_at)
VALUES ('{{ $json.email }}', '{{ $json.name }}', NOW())
ON DUPLICATE KEY UPDATE
  name = VALUES(name),
  updated_at = VALUES(updated_at)

Run that twice with the same email and the second run updates the existing row instead of inserting a copy. No unique key on the table? Then you must dedupe with the IF check in step 2 first, because a blind Insert will happily duplicate every row on every re-run.

4. Handle the two-way case

A genuine two-way sync needs both directions. One branch reads MySQL changes and writes them to the external app. A second reads the external app's changes and writes them back to MySQL. The tie-breaker when the same record changed on both sides is a last_modified timestamp: the newer write wins. The webhook-to-database workflow shows the inbound half of this, where an external app pushes changes in rather than n8n polling for them.

5. Log and alert on failure

A sync that fails silently is worse than no sync, because the data quietly diverges and you find out from a confused stakeholder. Wire an error branch that logs failed rows to Google Sheets and pings a channel. The Slack automation guide covers the alert side.

Implementation patterns

Pattern 1 — One-way mirror with upsert

Schedule Trigger
   → MySQL Select (WHERE updated_at > lastRun)
   → MySQL Insert (ON DUPLICATE KEY UPDATE)
   → Set lastRun = now

The simplest safe sync. The unique key plus the timestamp filter make re-runs idempotent. Run it a hundred times; the table stays clean.

Pattern 2 — Batched key comparison

MySQL Select (source changes)
   → MySQL Select (existing target keys, single query)
   → Code node (match in memory, split insert vs update)
   → MySQL Insert (new rows) + MySQL Update (changed rows)

This is the pattern for tables big enough that per-row queries hurt. The Code node does the set math; MySQL does the writes. In practice this is where most production syncs land.

Pattern 3 — Sheet-to-MySQL with validation

Google Sheets (read rows)
   → Code node (validate types, drop bad rows)
   → MySQL Insert (ON DUPLICATE KEY UPDATE)

Spreadsheets are full of dirty data: a phone number in the email column, a date typed as text. Validate before the write or MySQL rejects the batch and the whole run fails. The data validation workflow covers the cleanup step in detail.

n8n nodes you'll use most

NodePurpose
n8n-nodes-base.mySqlSelect, Insert, Update, Delete, and raw queries against MySQL/MariaDB
n8n-nodes-base.scheduleTrigger v1.2Drive the polling interval for the sync
n8n-nodes-base.if v2Gate writes and dedupe rows that already exist on the target
n8n-nodes-base.code v2Batch-match keys in memory and validate row types before writing
n8n-nodes-base.googleSheetsSource or sink for sync, plus a failure log for bad rows
The credential you should not reuse

Don't point your sync at the root MySQL user. Create a dedicated account scoped to exactly what the workflow touches: read-only for reporting syncs, read-write on specific tables for the ones that write back. A leaked n8n credential then can't drop your database. This is cheap insurance and the n8n community forum logs plenty of cases where it would have helped.

Getting started

  1. Create a MySQL credential in n8n with host, port, database, user, and password. Use a scoped account, not root.
  2. Confirm your target table has a unique key on whatever field identifies a row. Without it, upsert can't work.
  3. Add a Schedule Trigger and a MySQL Select filtered on a timestamp column.
  4. Store lastRun so each run reads only new changes.
  5. Decide one-way or two-way, and wire the upsert with ON DUPLICATE KEY UPDATE.
  6. Add a Code node for batched key comparison if the table is large.
  7. Add an error branch that logs failures and alerts a channel.

For an intake pipeline that already handles validation and multi-system writes, the Data Entry Automation Hub is the closest fit.

Browse the data templates →
Skip the build

The Data Entry Automation Hub ships the hard part of any sync — it collects submissions, validates and normalizes every field before it touches a system, then syncs the clean record to HubSpot CRM, a Trello board, and a master Google Sheet in one pass, with confirmation emails. Swap the Sheets sink for a MySQL Insert and you've got a validated database sync. It's part of The Complete n8n Templates Bundle, a one-time lifetime license to the whole catalog plus future templates, worth it if you run more than one data pipeline.

Get the Data Entry Automation Hub →

A MySQL sync is one of those workflows that looks done before it's safe. The upsert and the timestamp filter are what make it survive its second run and its hundredth. For adjacent patterns, the Google Sheets ETL workflow shows the extract-transform-load shape when a spreadsheet is the source, and the Airtable sync workflow covers two-way sync against a different backend. When you'd rather start from a working pipeline, the Smart To-Do List Manager demonstrates the same validate-then-write discipline against Trello and Sheets.

See all data automation templates →
FAQ

Common questions

How do I sync data to MySQL with n8n?
Add the MySQL node (n8n-nodes-base.mySql), create a credential with host, port, database, user, and password, then set the operation to Insert or Update. For sync workflows, pull source rows first, compare against existing keys, and write only the rows that changed.
Can n8n do a two-way sync between MySQL and another app?
Yes, but it takes two workflows or two branches: one that reads MySQL changes and pushes them out, and one that reads external changes and writes them back. A last-modified timestamp column on both sides decides which record wins when the same row changed in both places.
How do I avoid duplicate rows in an n8n MySQL sync?
Use an upsert. The MySQL node's Insert operation supports MySQL's ON DUPLICATE KEY UPDATE behavior when the table has a unique key, so a re-synced row updates in place instead of inserting a copy. Without a unique key, dedupe with an IF node before writing.
How often should an n8n MySQL sync run?
Most reporting and CRM syncs run on a Schedule Trigger every 5 to 15 minutes. Polling MySQL more often than the source data actually changes just burns executions. For near-real-time sync, trigger from the source app's webhook instead of polling.
Stop reading. Start running.

Get the workflow templates this guide is built on

Import-ready n8n JSON, step-by-step setup, and tested end-to-end. One-time payment, own it forever.

Free — $40 value

Get 3 tested n8n templates, free

The full customer package for three real catalog templates — workflow JSON, step-by-step setup guide, credential checklist. Built through the same live-instance release process as everything we sell. Plus new templates and automation guides in your inbox. No spam, unsubscribe anytime.

  • 01Smart To-Do List ManagerPre-built n8n workflow template that automates productivity with OpenAI. Live in about 10 minutes.$14
  • 02Email Follow-Up AutomatorPre-built n8n workflow template that automates crm with OpenAI. Live in about 15 minutes.$12
  • 03Market Trend AnalyzerPre-built n8n workflow template that automates data processing with OpenAI. Live in about 10 minutes.$14