Back to Blog
n8n
database-sync
postgresql
supabase
etl

How to Use n8n for Reliable Database Data Sync

October 8, 2026

Build reliable database synchronization pipelines with n8n across PostgreSQL, Supabase, and MySQL using idempotent upserts and CDC triggers.

How to Setup N8n Database Sync Workflows

Configuring an n8n database sync workflow requires establishing change detection triggers, data transformation mappings, and idempotent database write operations. Using n8n, developers can synchronize record states across PostgreSQL, Supabase, and MySQL databases without writing complex custom ETL software. Achieving strict data integrity demands robust polling or Change Data Capture (CDC) strategies, explicit upsert queries utilizing ON CONFLICT clauses, and isolated error handling sub-workflows for failed payload processing.


Architecture of a Reliable Database Synchronization Pipeline

Synchronizing data across heterogeneous databases using n8n requires isolating trigger ingestion, transformation logic, destination writing, and error logging into distinct pipeline stages.

+---------------------------------------------------------------------------------+
|                       N8N DATABASE SYNC ARCHITECTURE                            |
+---------------------------------------------------------------------------------+
|                                                                                 |
|  [ Source Database ]                                                            |
|  (PostgreSQL / Supabase / MySQL)                                                |
|         |                                                                       |
|         +---> Change Trigger (CDC Webhook / Timestamp Polling)                  |
|                     |                                                           |
|                     v                                                           |
|  [ n8n Workflow Engine ]                                                        |
|  +---------------------------------------------------------------------------+  |
|  | Node 1: Ingest Payload & Validate Schema Structure                       |  |
|  | Node 2: JavaScript Code Node (Data Normalization & Mapping)              |  |
|  | Node 3: Target Database Node (Idempotent Upsert / ON CONFLICT)            |  |
|  +---------------------------------------------------------------------------+  |
|                     |                                       |                   |
|           Success   v                             Error     v                   |
|    [ Destination Database ]                       [ Dead-Letter Queue / DB ]    |
|    (PostgreSQL / MySQL / Supabase)                (Error Log & Alerting)        |
|                                                                                 |
+---------------------------------------------------------------------------------+

Core Synchronization Principles

  1. At-Least-Once Delivery: Designing workflows to process payload deliveries multiple times without corrupting application state.
  2. Idempotency: Ensuring that executing a record update multiple times produces the exact same destination database state.
  3. Schema Normalization: Transforming source database data types (e.g., JSONB objects, timestamp strings, UUIDs, arrays) into formats accepted by target schema fields.
  4. Isolated Error Isolation: Routing failing records to a dead-letter log table without stopping the main workflow execution pipeline.

Step-by-Step Setup: Multi-Database Integration

Connecting source and target database instances within n8n involves choosing between incremental polling and event-driven Change Data Capture (CDC).

Approach A: Incremental Polling (updated_at Timestamp Tracking)

Incremental polling queries the source database periodically for records created or updated since the last execution run.

-- Source Database Query (PostgreSQL / MySQL)
SELECT 
    id, 
    first_name, 
    last_name, 
    email, 
    status, 
    updated_at 
FROM users 
WHERE updated_at > :last_sync_timestamp 
ORDER BY updated_at ASC 
LIMIT 1000;

Configuring Static Data in n8n

To persist the cursor timestamp between executions, use n8n's static workflow data store in a JavaScript Code node:

// Preserve sync timestamp across workflow runs using static data store
const staticData = $getWorkflowStaticData('global');
const lastSync = staticData.lastSyncTimestamp || '1970-01-01T00:00:00.000Z';

// Capture current execution timestamp
const currentSync = new Date().toISOString();

// Update static data for the next execution cycle
staticData.lastSyncTimestamp = currentSync;

return $input.all().map(item => {
    item.json._lastSyncUsed = lastSync;
    return item;
});

Approach B: Real-Time Event CDC (Supabase Webhooks & Postgres Listen)

For sub-second data synchronization, use database triggers or Change Data Capture engines that emit HTTP webhooks directly into an n8n Webhook node.

Supabase Database Webhook Trigger

Configure a Supabase Database Webhook to emit payload JSON on INSERT or UPDATE events:

{
  "type": "UPDATE",
  "table": "customers",
  "schema": "public",
  "record": {
    "id": "usr_99812",
    "email": "alex@example.com",
    "tier": "enterprise",
    "updated_at": "2026-10-08T14:30:00Z"
  },
  "old_record": {
    "id": "usr_99812",
    "email": "alex@example.com",
    "tier": "pro",
    "updated_at": "2026-10-01T09:00:00Z"
  }
}

PostgreSQL Native Triggers and LISTEN/NOTIFY

For self-hosted PostgreSQL instances, you can create a database trigger function that sends notifications when rows change:

-- Create trigger function for change notification
CREATE OR REPLACE FUNCTION notify_user_change()
RETURNS trigger AS $$
BEGIN
    PERFORM pg_notify(
        'user_updates',
        json_build_object(
            'action', TG_OP,
            'record', row_to_json(NEW)
        )::text
    );
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Attach trigger to target table
CREATE TRIGGER trigger_user_change
AFTER INSERT OR UPDATE ON users
FOR EACH ROW EXECUTE FUNCTION notify_user_change();

An n8n microservice or custom listener script can listen to the user_updates channel and dispatch HTTP post calls to n8n webhooks instantly.

For custom database pipeline design and backend server integrations, review our backend API development services.


Data Transformation Node Logic

Data schemas between disparate databases rarely match directly. Fields must be mapped, sanitized, and type-cast using n8n JavaScript Code nodes.

Code Node JavaScript Snippet: Complex Mapping & Data Sanitation

// Transform source PostgreSQL/Supabase record to target MySQL/PostgreSQL schema
const transformedItems = [];

for (const item of $input.all()) {
    const record = item.json.record || item.json;
    
    // Normalize timestamps to standard ISO string format
    const formattedUpdatedAt = record.updated_at 
        ? new Date(record.updated_at).toISOString() 
        : new Date().toISOString();

    // Serialize nested JSON objects for databases expecting text/JSON columns
    let metadataString = '{}';
    if (record.metadata && typeof record.metadata === 'object') {
        metadataString = JSON.stringify(record.metadata);
    } else if (typeof record.metadata === 'string') {
        metadataString = record.metadata;
    }

    // Convert array attributes into delimited strings or clean arrays
    const userTags = Array.isArray(record.tags) 
        ? record.tags.join(',') 
        : (record.tags || '');

    transformedItems.push({
        json: {
            external_id: String(record.id),
            full_name: `${record.first_name || ''} ${record.last_name || ''}`.trim(),
            user_email: String(record.email || '').toLowerCase().trim(),
            account_status: record.status || 'pending',
            tag_list: userTags,
            custom_metadata: metadataString,
            synced_at: new Date().toISOString(),
            source_updated_at: formattedUpdatedAt
        }
    });
}

return transformedItems;

Executing Idempotent Upserts Across Databases

Preventing duplicate key violations and data overwrites requires constructing explicit upsert statements in target database nodes.

PostgreSQL & Supabase Upsert (ON CONFLICT)

In the PostgreSQL node, select the Execute Query action and execute an ON CONFLICT statement:

INSERT INTO target_customers (
    external_id, 
    full_name, 
    user_email, 
    account_status, 
    tag_list,
    custom_metadata, 
    synced_at, 
    source_updated_at
) VALUES (
    $1, $2, $3, $4, $5, $6, $7, $8
)
ON CONFLICT (external_id) 
DO UPDATE SET 
    full_name = EXCLUDED.full_name,
    user_email = EXCLUDED.user_email,
    account_status = EXCLUDED.account_status,
    tag_list = EXCLUDED.tag_list,
    custom_metadata = EXCLUDED.custom_metadata,
    synced_at = EXCLUDED.synced_at,
    source_updated_at = EXCLUDED.source_updated_at
WHERE target_customers.source_updated_at < EXCLUDED.source_updated_at;

Note: The conditional clause WHERE target_customers.source_updated_at < EXCLUDED.source_updated_at ensures that out-of-order webhook events cannot overwrite newer destination data with older state.

MySQL Upsert (ON DUPLICATE KEY UPDATE)

In the MySQL node, execute an equivalent ON DUPLICATE KEY UPDATE query:

INSERT INTO target_customers (
    external_id, 
    full_name, 
    user_email, 
    account_status, 
    tag_list,
    custom_metadata, 
    synced_at, 
    source_updated_at
) VALUES (
    {{ $json.external_id }}, 
    {{ $json.full_name }}, 
    {{ $json.user_email }}, 
    {{ $json.account_status }}, 
    {{ $json.tag_list }},
    {{ $json.custom_metadata }}, 
    {{ $json.synced_at }}, 
    {{ $json.source_updated_at }}
)
ON DUPLICATE KEY UPDATE 
    full_name = VALUES(full_name),
    user_email = VALUES(user_email),
    account_status = VALUES(account_status),
    tag_list = VALUES(tag_list),
    custom_metadata = VALUES(custom_metadata),
    synced_at = VALUES(synced_at),
    source_updated_at = VALUES(source_updated_at);

For custom database synchronization setups and complex multi-source ETL pipelines, explore our system integration services.


Error Handling, Retries & Dead-Letter Queues

Network blips, database lock timeouts, and schema validation failures will occur in production. A resilient n8n sync pipeline handles errors gracefully without dropping data.

                                +-------------------+
                                | Primary Sync Node |
                                +-------------------+
                                          |
                        +-----------------+-----------------+
                        |                                   |
                  Success v                           Failure v
        +-----------------------+           +-------------------------------+
        | Destination DB Written|           | Error Trigger Node            |
        +-----------------------+           | (Captures Execution Payload)  |
                                            +-------------------------------+
                                                            |
                                                            v
                                            +-------------------------------+
                                            | Dead-Letter Queue (DLQ Table) |
                                            | (Logs Payload & Stack Trace)  |
                                            +-------------------------------+
                                                            |
                                                            v
                                            +-------------------------------+
                                            | Replay Sub-Workflow           |
                                            | (Re-Executes Failed Sync)     |
                                            +-------------------------------+

1. Enabling Node-Level Retry Rules

Inside n8n node settings, enable built-in retry mechanics for target database connections:

  • Number of Retries: 3 to 5 attempts.
  • Retry Interval: 2000 ms (2 seconds).
  • On Fail: Route to error workflow (Continue On Fail or Error Trigger).

2. Implementing a Sub-Workflow Error Handler

Create a secondary workflow dedicated to logging error payloads to a dead_letter_queue table and notifying engineering teams.

Error Log Query:

INSERT INTO sync_dead_letter_queue (
    workflow_id, 
    workflow_name, 
    failed_node, 
    error_message, 
    payload_json, 
    failed_at
) VALUES (
    $1, $2, $3, $4, $5, NOW()
);

3. Automated Replay Mechanism for Dead-Letter Logs

Once temporary target database issues are resolved, run an automated replay workflow in n8n that reads records from sync_dead_letter_queue where status = 'pending', re-executes the upsert query, and updates status = 'resolved'.


Performance Monitoring & Batch Optimization

Synchronizing large database tables containing millions of rows requires optimizing batch sizes and index strategies.

Batch Processing Configuration

Rather than executing single database inserts sequentially in a loop, process incoming arrays using n8n's Split In Batches node.

  • Batch Size: 500 to 1,000 items per database transaction block.
  • Memory Management: Avoid loading over 50,000 items into memory simultaneously in a single node; process data using paginated queries.

Index Optimization Strategy

Ensure that all fields used in ON CONFLICT clauses, WHERE timestamp filters, or join conditions possess database indexes on both source and target servers:

-- Create unique index for idempotent conflict resolution
CREATE UNIQUE INDEX IF NOT EXISTS idx_target_cust_ext_id ON target_customers(external_id);

-- Create timestamp index for high-speed incremental polling
CREATE INDEX IF NOT EXISTS idx_source_users_updated_at ON users(updated_at);

To build analytics dashboards that track database sync latency and system performance, explore our custom dashboard development services.


Common Implementation Mistakes to Avoid

Building database synchronization workflows with n8n requires avoiding several operational pitfalls:

1. Polling Without Indexing the Timestamp Column

Executing incremental polling queries (WHERE updated_at > :last_sync) without an index on updated_at forces full table scans on the source database, causing query timeouts as the database grows.

2. Missing Unique Constraints on Target Database Keys

Executing ON CONFLICT (external_id) in PostgreSQL without a UNIQUE constraint or unique index on external_id causes database execution errors. Always verify schema constraints before running upsert workflows.

3. Ignoring Out-of-Order Delivery Race Conditions

Webhook payloads can arrive out of order due to network latency. Failing to include timestamp comparison clauses (WHERE target.source_updated_at < EXCLUDED.source_updated_at) can cause older updates to overwrite newer database states.

4. Hardcoding Database Credentials in Node Parameters

Never hardcode database strings or secrets inside Code nodes or query parameters. Always use n8n's encrypted Credential Vault to store database connection details.


Frequently Asked Questions

Is n8n fast enough for high-volume database synchronization?

Yes. When deployed in Redis Queue Mode with worker nodes and optimized batch queries, n8n can process thousands of database records per second. For multi-million record initial bulk loads, standard ETL bulk copy tools (such as pg_dump or mysqldump) should be paired with n8n for real-time incremental updates.

How does n8n prevent duplicate records during database sync?

n8n prevents duplicates by combining schema unique constraints with idempotent upsert queries (ON CONFLICT in PostgreSQL/Supabase or ON DUPLICATE KEY UPDATE in MySQL).

Can n8n sync data bidirectionally between two databases?

Yes. Bidirectional sync requires setting up workflow pipelines in both directions. To avoid infinite loops, each workflow must exclude updates triggered by the sync process itself (e.g., filtering out updates where updated_by = 'n8n_sync_service').

What happens if the target database goes offline during a sync?

If the target database goes offline, n8n's retry settings attempt reconnects. If retries are exhausted, the workflow routes the execution payload to a dead-letter queue or error trigger, allowing engineers to replay failed sync tasks once database connectivity is restored.


Modernize Your Database Synchronization Infrastructure

Building reliable cross-database synchronization pipelines requires rigorous architecture, schema handling, and error-recovery engineering. Sharcon designs resilient n8n automation workflows, multi-database sync engines, and custom API pipelines for growing companies.

To build a secure, real-time data synchronization pipeline, explore our custom system integration services.

Max Lebedev

Max Lebedev

CEO of Sharcon LLC

15+ years in marketing and development. Leading a team of 30+ professionals at Sharcon.