AutomationFlowsData & Sheets › Synchronize Excel or Google Sheets with Postgres (bi-directional)

Synchronize Excel or Google Sheets with Postgres (bi-directional)

ByLenouar @aureusr on n8n.io

This workflow is perfect for companies that have always managed their operations in Excel or Google Sheets and want to gradually transition to using a database or custom software. It ensures business continuity while modernizing data management. Trigger options → Run the sync…

Event trigger★★★★☆ complexity8 nodesExecute Workflow TriggerMicrosoft ExcelPostgres
Data & Sheets Trigger: Event Nodes: 8 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #8457 — we link there as the canonical source.

This workflow follows the Execute Workflow Trigger → Postgres recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "nodes": [
    {
      "id": "e6bc660d-99b9-4321-ad7b-73f4b73a80f9",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -656,
        -320
      ],
      "parameters": {
        "color": 6,
        "width": 976,
        "height": 752,
        "content": "## Synchronize Excel Sheet with Postgres Database"
      },
      "typeVersion": 1
    },
    {
      "id": "9a97bb0b-9428-424f-952c-0d83dca0e94f",
      "name": "Sanitize Date",
      "type": "n8n-nodes-base.code",
      "position": [
        -96,
        16
      ],
      "parameters": {
        "jsCode": "return items.map(item => {\n  const inputDate = item.json[\"date\"];\n  let formattedDate = null;\n  let dateObj;\n\n  // Handle Excel serial number\n  if (typeof inputDate === \"number\") {\n    const baseDate = new Date(Date.UTC(1899, 11, 30));\n    baseDate.setDate(baseDate.getDate() + inputDate);\n    dateObj = baseDate;\n  }\n\n  // Handle string format\n  else if (typeof inputDate === \"string\") {\n    const parsed = new Date(inputDate);\n    if (!isNaN(parsed)) {\n      dateObj = parsed;\n    }\n  }\n\n  // Format date as MM/DD/YYYY\n  if (dateObj) {\n    const M = String(dateObj.getMonth() + 1).padStart(2, '0');\n    const D = String(dateObj.getDate()).padStart(2, '0');\n    const Y = dateObj.getFullYear();\n    formattedDate = `${M}/${D}/${Y}`;\n  }\n\n  item.json[\"date\"] = formattedDate;\n  return { json: item.json };\n});\n"
      },
      "typeVersion": 2
    },
    {
      "id": "71ef2eee-cb60-48ad-9050-df5a27f1a568",
      "name": "Click Sync Excel -> DB",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -576,
        -176
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "a3a85552-617f-4aa2-b29f-2d7196c7148c",
      "name": "Exec Sync Excel -> DB",
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "position": [
        -576,
        16
      ],
      "parameters": {
        "inputSource": "passthrough"
      },
      "typeVersion": 1.1
    },
    {
      "id": "a17a319e-ef1e-4a2a-b5f4-302628b811c5",
      "name": "Schedule Sync Excel -> DB",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -576,
        208
      ],
      "parameters": {
        "rule": {
          "interval": [
            {}
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "705234f6-2f3b-45fd-a072-09da5ea65ce1",
      "name": "Get Table",
      "type": "n8n-nodes-base.microsoftExcel",
      "position": [
        -256,
        16
      ],
      "parameters": {
        "limit": 200,
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": "Clients"
        },
        "filters": {},
        "resource": "table",
        "workbook": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": ""
        },
        "operation": "getRows",
        "worksheet": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": "CLIENTS"
        }
      },
      "credentials": {
        "microsoftExcelOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "b674a99d-e5a1-45d4-9092-e9b66a7943f8",
      "name": "Upsert Table",
      "type": "n8n-nodes-base.postgres",
      "position": [
        80,
        16
      ],
      "parameters": {
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "clients",
          "cachedResultName": "clients"
        },
        "schema": {
          "__rl": true,
          "mode": "list",
          "value": "public",
          "cachedResultName": "public"
        },
        "columns": {
          "value": {
            "no": 0
          },
          "schema": [
            {
              "id": "no",
              "type": "number",
              "display": true,
              "removed": false,
              "required": true,
              "displayName": "no",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [
            "no"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "upsert"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "30ed085e-d0ba-4271-a92e-61dece670542",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1264,
        -320
      ],
      "parameters": {
        "width": 592,
        "height": 752,
        "content": "## Excel \u2194 Database Synchronization\n\nThis workflow automates syncing between **Excel/Google Sheets** and a **Postgres DB**:  \n\n1. **Triggers** \u2192 Run manually, on schedule, or via another workflow.  \n2. **Fetch Data** \u2192 Reads rows from an Excel/Google Sheet table.  \n3. **Sanitize** \u2192 Fixes date formats and cleans data.  \n4. **Upsert into DB** \u2192 Ensures new/updated rows are saved without duplicates.  \n   - Column names in Excel/Sheets and the DB must **match exactly** for auto-mapping.  \n   - If not, you can manually map columns in the Postgres node.  \n5. **Two-way sync possible** \u2192 Extend workflow to push DB changes back into Excel.  \n\n\u26a1 **Use Case:** Perfect for companies relying heavily on spreadsheets but planning to transition toward dashboards or SaaS software. It keeps Excel in use while persisting data in a structured database.  \n\n"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Get Table": {
      "main": [
        [
          {
            "node": "Sanitize Date",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sanitize Date": {
      "main": [
        [
          {
            "node": "Upsert Table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Exec Sync Excel -> DB": {
      "main": [
        [
          {
            "node": "Get Table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Click Sync Excel -> DB": {
      "main": [
        [
          {
            "node": "Get Table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Sync Excel -> DB": {
      "main": [
        [
          {
            "node": "Get Table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow is perfect for companies that have always managed their operations in Excel or Google Sheets and want to gradually transition to using a database or custom software. It ensures business continuity while modernizing data management. Trigger options → Run the sync…

Source: https://n8n.io/workflows/8457/ — original creator credit. Request a take-down →

More Data & Sheets workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Data & Sheets

Agendamiento_v2. Uses n8n-nodes-evolution-api, redis, httpRequest, executeWorkflowTrigger. Event-driven trigger; 59 nodes.

N8N Nodes Evolution Api, Redis, HTTP Request +3
Data & Sheets

Cancelacion_v2. Uses executeWorkflowTrigger, redis, httpRequest, n8n-nodes-evolution-api. Event-driven trigger; 46 nodes.

Execute Workflow Trigger, Redis, HTTP Request +3
Data & Sheets

Youtube Searcher. Uses splitInBatches, httpRequest, manualTrigger, executeWorkflowTrigger. Event-driven trigger; 21 nodes.

HTTP Request, Execute Workflow Trigger, Postgres +1
Data & Sheets

QuepasaAutomatic. Uses postgres, executeWorkflowTrigger. Event-driven trigger; 20 nodes.

Postgres, Execute Workflow Trigger
Data & Sheets

Log errors and avoid sending too many emails. Uses errorTrigger, postgres, stickyNote, emailSend. Event-driven trigger; 16 nodes.

Error Trigger, Postgres, Email Send +2