AutomationFlowsDevOps › GitHub Workflow Sync Automation

GitHub Workflow Sync Automation

Original n8n title: Sync

Sync. Uses github, n8n. Event-driven trigger; 17 nodes.

Event trigger★★★★☆ complexity17 nodesGitHubn8n
DevOps Trigger: Event Nodes: 17 Complexity: ★★★★☆ Added:

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": "af28f832-7867-4959-82d8-45e9790cfe10",
      "name": "On clicking 'execute'",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        635,
        295
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "e97be892-be53-4416-af07-8c26c67b17f6",
      "name": "GitHub",
      "type": "n8n-nodes-base.github",
      "position": [
        1480,
        260
      ],
      "parameters": {
        "owner": "={{$node[\"Globals\"].json[\"repo\"][\"owner\"]}}",
        "filePath": "={{$node[\"Globals\"].json[\"repo\"][\"path\"]}}{{$json[\"name\"]}}.json",
        "resource": "file",
        "operation": "get",
        "repository": "={{$node[\"Globals\"].json[\"repo\"][\"name\"]}}",
        "asBinaryProperty": false,
        "additionalParameters": {}
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1,
      "continueOnFail": true,
      "alwaysOutputData": true
    },
    {
      "id": "6b5d5c0f-ae4b-4c11-a97b-840fcddc1178",
      "name": "github_status",
      "type": "n8n-nodes-base.switch",
      "position": [
        2120,
        400
      ],
      "parameters": {
        "rules": {
          "rules": [
            {
              "value2": "same"
            },
            {
              "output": 1,
              "value2": "different"
            },
            {
              "output": 2,
              "value2": "new"
            }
          ]
        },
        "value1": "={{$json[\"github_status\"]}}",
        "dataType": "string"
      },
      "typeVersion": 1
    },
    {
      "id": "e91007ac-876b-4919-bc8b-a007edf0766f",
      "name": "same",
      "type": "n8n-nodes-base.noOp",
      "position": [
        2360,
        200
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "123ca083-de40-40c5-8ea0-d17d3e6c3776",
      "name": "different",
      "type": "n8n-nodes-base.noOp",
      "position": [
        2360,
        400
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "f798adff-42c7-400c-9f42-ca913d631ec2",
      "name": "new",
      "type": "n8n-nodes-base.noOp",
      "position": [
        2360,
        580
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "5526efe9-c7fd-4cd5-8ba6-adda3b9c6629",
      "name": "GitHub Edit",
      "type": "n8n-nodes-base.github",
      "position": [
        2540,
        400
      ],
      "parameters": {
        "owner": "={{$node[\"Globals\"].json[\"repo\"][\"owner\"]}}",
        "filePath": "={{$node[\"Globals\"].json[\"repo\"][\"path\"]}}{{$node[\"OneAtATime\"].json[\"name\"]}}.json",
        "resource": "file",
        "operation": "edit",
        "repository": "={{$node[\"Globals\"].json[\"repo\"][\"name\"]}}",
        "fileContent": "={{$node[\"isDiffOrNew\"].json[\"n8n_data_stringy\"]}}",
        "commitMessage": "=[N8N Backup] {{$node[\"OneAtATime\"].json[\"name\"]}}.json ({{$json[\"github_status\"]}})"
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "0e919b9b-a810-4fe4-be21-f18d11d15a25",
      "name": "GitHub Create",
      "type": "n8n-nodes-base.github",
      "position": [
        2540,
        580
      ],
      "parameters": {
        "owner": "={{$node[\"Globals\"].json[\"repo\"][\"owner\"]}}",
        "filePath": "={{$node[\"Globals\"].json[\"repo\"][\"path\"]}}{{$node[\"OneAtATime\"].json[\"name\"]}}.json",
        "resource": "file",
        "repository": "={{$node[\"Globals\"].json[\"repo\"][\"name\"]}}",
        "fileContent": "={{$node[\"isDiffOrNew\"].json[\"n8n_data_stringy\"]}}",
        "commitMessage": "=[N8N Backup] {{$node[\"OneAtATime\"].json[\"name\"]}}.json ({{$json[\"github_status\"]}})"
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "4ffbe161-ac7b-4786-a06e-07679bc8ab6d",
      "name": "OneAtATime",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        1260,
        420
      ],
      "parameters": {
        "options": {},
        "batchSize": 1
      },
      "typeVersion": 1
    },
    {
      "id": "3ca3580c-fa09-4669-8d9e-4ed5c0032d27",
      "name": "Globals",
      "type": "n8n-nodes-base.set",
      "position": [
        820,
        420
      ],
      "parameters": {
        "values": {
          "string": [
            {
              "name": "repo.owner",
              "value": "birdgg"
            },
            {
              "name": "repo.name",
              "value": "n8n-workflows"
            },
            {
              "name": "repo.path",
              "value": "workflows/"
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "9815a578-3e63-4128-bea5-8301745606b7",
      "name": "Every 12 hours",
      "type": "n8n-nodes-base.cron",
      "position": [
        635,
        555
      ],
      "parameters": {
        "triggerTimes": {
          "item": [
            {
              "mode": "everyX",
              "value": 12
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "fbdcfdd9-4f07-48e7-8586-092d67c7fe59",
      "name": "Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        180,
        200
      ],
      "parameters": {
        "width": 389.78906250000017,
        "height": 464.79920462713443,
        "content": "## Workflow Backups\nThis workflow will automatically backup your workflows to your Github account every 12 hours.\n\n### Setup\nOpen Globals and update the values below\n**repo.owner:** This is your Github username\n**repo.name:** This is the name of your repository\n**repo.path:** This is the folder to use within the repository, If it doesn't exist it will be created.\n\nIf your username was `n8n-io` and your repository was called `n8n-backups` and you wanted the workflows to go into a `workflows` folder you would set:\n\nrepo.owner - n8n-io\nrepo.name - n8n-backups\nrepo.path - workflows"
      },
      "typeVersion": 1
    },
    {
      "id": "29078a78-b383-48da-be23-2d959fd82ada",
      "name": "Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1240,
        140
      ],
      "parameters": {
        "width": 755.2349999999991,
        "height": 424.04333333333386,
        "content": "## Single Item Loop\nWe process each workflow item one at a time, We first check Github to see if a file exists then we merge the Github Data and the API item so we can check if the values match in the function node `isDiffOrNew` we then set a status of `same`, `different` or `new`"
      },
      "typeVersion": 1
    },
    {
      "id": "cc2c5992-4fec-4374-be41-a6f92149f3ba",
      "name": "Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2080,
        80
      ],
      "parameters": {
        "width": 666.4416666666662,
        "height": 693.9283333333326,
        "content": "## Save the data\nUsing the switch node we work out what to do based on the previous status,  If it is the same we do nothing, If it is different we update the file in Github and if it is new we create a new file. After this is done we loop back to the Split in Batches node (OneAtAtTime) to start the process again."
      },
      "typeVersion": 1
    },
    {
      "id": "926f0d45-31e5-4322-a9d8-36b8f8020076",
      "name": "n8n",
      "type": "n8n-nodes-base.n8n",
      "position": [
        1020,
        420
      ],
      "parameters": {
        "filters": {}
      },
      "credentials": {
        "n8nApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "17871023-92b4-422d-863d-e5c271fe413b",
      "name": "isDiffOrNew",
      "type": "n8n-nodes-base.code",
      "position": [
        1880,
        400
      ],
      "parameters": {
        "jsCode": "// File Returned with Content\nif (Object.keys($input.all()[0].json).includes(\"content\")) {\n  // Get JSON Objects\n  var origWorkflow = JSON.parse(Buffer.from($input.all()[0].json.content, 'base64'));\n  var n8nWorkflow = $input.all()[1].json;\n  \n  // Order JSON Objects\n  var orderedOriginal = {}\n  var orderedActual = {}\n  \n  Object.keys(origWorkflow).sort().forEach(function(key) {\n    orderedOriginal[key] = origWorkflow[key];\n  });\n  \n  Object.keys(n8nWorkflow).sort().forEach(function(key) {\n    orderedActual[key] = n8nWorkflow[key];\n  });\n\n  // Determine Difference\n  if ( JSON.stringify(orderedOriginal) === JSON.stringify(orderedActual) ) {\n    $input.all()[0].json.github_status = \"same\";\n    $input.all()[0].json.content_decoded = orderedOriginal;\n  } else {\n    $input.all()[0].json.github_status = \"different\";\n    $input.all()[0].json.content_decoded = orderedOriginal;\n    $input.all()[0].json.n8n_data_stringy = JSON.stringify(orderedActual, null, 2);\n  }\n// No File Returned / New Workflow\n} else {\n  // Order JSON Object\n  var n8nWorkflow = ($input.all()[1].json);\n  var orderedActual = {}\n  Object.keys(n8nWorkflow).sort().forEach(function(key) {\n    orderedActual[key] = n8nWorkflow[key];\n  });\n  \n  // Proper Formatting\n  $input.all()[0].json.github_status = \"new\";\n  $input.all()[0].json.n8n_data_stringy = JSON.stringify(orderedActual, null, 2);\n}\n\n// Return Items\nreturn $input.all();"
      },
      "typeVersion": 1
    },
    {
      "id": "581f2f11-c853-41f2-bc02-c3d8dcc57f19",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        1700,
        400
      ],
      "parameters": {},
      "typeVersion": 2
    }
  ],
  "connections": {
    "n8n": {
      "main": [
        [
          {
            "node": "OneAtATime",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "new": {
      "main": [
        [
          {
            "node": "GitHub Create",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "same": {
      "main": [
        [
          {
            "node": "OneAtATime",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "isDiffOrNew",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GitHub": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Globals": {
      "main": [
        [
          {
            "node": "n8n",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "different": {
      "main": [
        [
          {
            "node": "GitHub Edit",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OneAtATime": {
      "main": [
        [
          {
            "node": "GitHub",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "GitHub Edit": {
      "main": [
        [
          {
            "node": "OneAtATime",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "isDiffOrNew": {
      "main": [
        [
          {
            "node": "github_status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GitHub Create": {
      "main": [
        [
          {
            "node": "OneAtATime",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "github_status": {
      "main": [
        [
          {
            "node": "same",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "different",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "new",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Every 12 hours": {
      "main": [
        [
          {
            "node": "Globals",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "On clicking 'execute'": {
      "main": [
        [
          {
            "node": "Globals",
            "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

How this works

This workflow automates the synchronisation of your local files with a GitHub repository, ensuring your code or documents stay up to date without manual uploads. It's ideal for developers or teams managing projects who want to streamline version control and collaboration. The key step involves comparing local changes against the repository via GitHub integration, then either editing existing files or creating new ones as needed, all triggered by a simple event.

Use this workflow when you frequently update files and need reliable, automated backups to GitHub, such as during daily coding sessions or content revisions. Avoid it for very large repositories where sync times might exceed practical limits, or if you require complex branching logic beyond basic edits and creations. Common variations include adding email notifications for sync completion or integrating with tools like Google Drive for broader file sources.

About this workflow

Sync. Uses github, n8n. Event-driven trigger; 17 nodes.

Source: https://github.com/birdgg/n8n-workflows/blob/2e388afebf41331f4ba322410faaecc579e5a608/workflows/sync.json — original creator credit. Request a take-down →

More DevOps workflows → · Browse all categories →

Related workflows

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

DevOps

This template lets you selectively import n8n workflows from a GitHub repository, even when your repository uses deeply nested folder structures.

Form Trigger, GitHub, n8n +1
DevOps

Code Github. Uses manualTrigger, stickyNote, n8n, httpRequest. Event-driven trigger; 25 nodes.

n8n, HTTP Request, GitHub +1
DevOps

Code Github. Uses manualTrigger, stickyNote, n8n, httpRequest. Event-driven trigger; 23 nodes.

n8n, HTTP Request, GitHub +1
DevOps

Restore Backed Up Workflows From Github To N8N. Uses manualTrigger, stickyNote, noOp, github. Event-driven trigger; 17 nodes.

GitHub, n8n
DevOps

[OPS] Restore workflows from GitHub to n8n. Uses manualTrigger, stickyNote, noOp, github. Event-driven trigger; 17 nodes.

GitHub, n8n