{
  "id": "iEc3aM4RGhZTp8t3",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Backup & Restore n8n Workflows and Credentials with GitHub",
  "tags": [],
  "nodes": [
    {
      "id": "09d2ef5e-5774-496b-8d17-cea09d15e8c9",
      "name": "Loop: Single WF",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        6608,
        1584
      ],
      "parameters": {
        "options": {},
        "batchSize": 20
      },
      "typeVersion": 3
    },
    {
      "id": "4892c050-5469-4f44-8d2b-bd692e2c6403",
      "name": "Loop: Single Cred",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        6624,
        2112
      ],
      "parameters": {
        "options": {},
        "batchSize": 20
      },
      "typeVersion": 3
    },
    {
      "id": "306275a9-8db3-4e7d-b94f-ad6171722db3",
      "name": "Trigger Backup",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        4816,
        1696
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 10,22 * * *"
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "838f96ca-628e-4dfd-8cca-f8e33c515587",
      "name": "Init Workflow Folder",
      "type": "n8n-nodes-base.github",
      "maxTries": 3,
      "position": [
        5488,
        1696
      ],
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $(\"Backup Configuration\").first().json.githubOwner }}",
          "cachedResultName": "Configured GitHub owner"
        },
        "filePath": "={{ $json.date }}/workflows/readme.md",
        "resource": "file",
        "repository": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $(\"Backup Configuration\").first().json.githubRepository }}",
          "cachedResultName": "Configured backup repository"
        },
        "fileContent": "=Backup {{ $json.date }}",
        "commitMessage": "=Initialize workflow backup folder for {{ $json.date }}"
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1.1,
      "waitBetweenTries": 2000
    },
    {
      "id": "aa904698-c729-42a7-b1be-d6f851c49b96",
      "name": "Init Creds Folder",
      "type": "n8n-nodes-base.github",
      "maxTries": 3,
      "position": [
        5712,
        1984
      ],
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $(\"Backup Configuration\").first().json.githubOwner }}",
          "cachedResultName": "Configured GitHub owner"
        },
        "filePath": "={{ $json.content.path.split('/').slice(0, -2).join('/') }}/credentials/readme.md",
        "resource": "file",
        "repository": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $(\"Backup Configuration\").first().json.githubRepository }}",
          "cachedResultName": "Configured backup repository"
        },
        "fileContent": "=Backup {{ $json.date }}",
        "commitMessage": "=Initialize credential backup folder"
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1.1,
      "waitBetweenTries": 2000
    },
    {
      "id": "751437f1-700d-43cd-81e7-bb9563e70342",
      "name": "Export Workflows",
      "type": "n8n-nodes-base.ssh",
      "maxTries": 2,
      "position": [
        5936,
        1504
      ],
      "parameters": {
        "command": "=docker exec {{ $(\"Backup Configuration\").first().json.dockerContainer }} sh -c \"n8n export:workflow --all --output=/tmp/workflows.json >/dev/null && cat /tmp/workflows.json\""
      },
      "credentials": {
        "sshPassword": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1,
      "waitBetweenTries": 1500
    },
    {
      "id": "4e93c946-1ef4-4eb5-b5f6-780b0b464197",
      "name": "Export Credentials",
      "type": "n8n-nodes-base.ssh",
      "maxTries": 2,
      "position": [
        5936,
        1984
      ],
      "parameters": {
        "command": "=docker exec {{ $(\"Backup Configuration\").first().json.dockerContainer }} sh -c \"n8n export:credentials --all --output=/tmp/credentials.json >/dev/null && cat /tmp/credentials.json\""
      },
      "credentials": {
        "sshPassword": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1,
      "waitBetweenTries": 1500
    },
    {
      "id": "6648b03e-a3e6-41f7-9799-2e55cdbc4511",
      "name": "Parse Workflow Export",
      "type": "n8n-nodes-base.code",
      "position": [
        6160,
        1504
      ],
      "parameters": {
        "jsCode": "const workflows = JSON.parse($input.first().json.stdout);\n\nreturn [{ json: { workflows } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "3a31f731-3ed4-4afe-b500-89c5b17073e1",
      "name": "Parse Creds Export",
      "type": "n8n-nodes-base.code",
      "position": [
        6160,
        1984
      ],
      "parameters": {
        "jsCode": "const credentials = JSON.parse($input.first().json.stdout);\n\nreturn [{ json: { credentials } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "9c0631b3-a4af-4265-8154-5045a396cd56",
      "name": "Split Workflows",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        6384,
        1504
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "workflows"
      },
      "typeVersion": 1
    },
    {
      "id": "bc609884-b1a0-475d-9090-842f3bcd706a",
      "name": "Split Credentials",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        6384,
        1984
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "credentials"
      },
      "typeVersion": 1
    },
    {
      "id": "bdf87501-215d-42d9-ad4e-37a0a7a11317",
      "name": "Convert All WFs to JSON",
      "type": "n8n-nodes-base.convertToFile",
      "position": [
        6624,
        1136
      ],
      "parameters": {
        "options": {
          "fileName": "workflows-all.json"
        },
        "operation": "toJson"
      },
      "typeVersion": 1.1
    },
    {
      "id": "eb2270fb-9c37-4f1a-820a-4ec27d6fae3c",
      "name": "Convert Single WF to JSON",
      "type": "n8n-nodes-base.convertToFile",
      "position": [
        6832,
        1552
      ],
      "parameters": {
        "mode": "each",
        "options": {
          "fileName": "={{ $json.name }}.json"
        },
        "operation": "toJson"
      },
      "typeVersion": 1.1
    },
    {
      "id": "585857ff-69f6-4555-9d9c-5effe6bc9502",
      "name": "Prep All WFs Payload",
      "type": "n8n-nodes-base.code",
      "position": [
        6848,
        1136
      ],
      "parameters": {
        "jsCode": "const path = $(\"Init Workflow Folder\").first().json.content.path;\nconst folder = path.split('/').slice(0, -2).join('/');\n\nreturn $input.all().map(item => ({\n  json: {\n    ...item.json,\n    path,\n    folder\n  },\n  binary: item.binary\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "f2077044-7ab8-496e-8ce5-a6bf3debe98d",
      "name": "Push Single Cred",
      "type": "n8n-nodes-base.github",
      "maxTries": 3,
      "position": [
        7296,
        2160
      ],
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $(\"Backup Configuration\").first().json.githubOwner }}",
          "cachedResultName": "Configured GitHub owner"
        },
        "filePath": "={{ $json.folder + '/' + $binary.data.fileName }}",
        "resource": "file",
        "binaryData": true,
        "repository": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $(\"Backup Configuration\").first().json.githubRepository }}",
          "cachedResultName": "Configured backup repository"
        },
        "commitMessage": "=Back up credential {{ $binary.data.fileName }}"
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1.1,
      "waitBetweenTries": 2000
    },
    {
      "id": "10b38984-7dd0-40ba-b3cc-e14c9c878a74",
      "name": "Push Single WF",
      "type": "n8n-nodes-base.github",
      "maxTries": 3,
      "position": [
        7280,
        1632
      ],
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $(\"Backup Configuration\").first().json.githubOwner }}",
          "cachedResultName": "Configured GitHub owner"
        },
        "filePath": "={{ $json.folder + '/' + $binary.data.fileName }}",
        "resource": "file",
        "binaryData": true,
        "repository": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $(\"Backup Configuration\").first().json.githubRepository }}",
          "cachedResultName": "Configured backup repository"
        },
        "commitMessage": "=Back up workflow {{ $binary.data.fileName }}"
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1.1,
      "waitBetweenTries": 2000
    },
    {
      "id": "d7f8f0e5-bf60-4415-83b0-71d85d02ffc9",
      "name": "Prep Single WF Payload",
      "type": "n8n-nodes-base.code",
      "position": [
        7056,
        1552
      ],
      "parameters": {
        "jsCode": "const path = $(\"Init Workflow Folder\").first().json.content.path;\nconst folder = path.split('/').slice(0, -1).join('/');\n\nreturn $input.all().map(item => ({\n  json: {\n    path,\n    folder,\n  },\n  binary: item.binary,\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "dc80aab3-6c16-4b71-b218-e74b1a256a60",
      "name": "Prep Single Cred Payload",
      "type": "n8n-nodes-base.code",
      "position": [
        7072,
        2080
      ],
      "parameters": {
        "jsCode": "const path = $(\"Init Creds Folder\").first().json.content.path;\nconst folder = path.split('/').slice(0, -1).join('/');\n\nreturn $input.all().map(item => ({\n  json: {\n    ...item.json,\n    path,\n    folder\n  },\n  binary: item.binary\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "15016299-0411-4c7b-8eaf-1aa45bad7152",
      "name": "Push All WFs",
      "type": "n8n-nodes-base.github",
      "maxTries": 3,
      "position": [
        7072,
        1136
      ],
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $(\"Backup Configuration\").first().json.githubOwner }}",
          "cachedResultName": "Configured GitHub owner"
        },
        "filePath": "={{ $json.folder + '/' + $binary.data.fileName }}",
        "resource": "file",
        "binaryData": true,
        "repository": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $(\"Backup Configuration\").first().json.githubRepository }}",
          "cachedResultName": "Configured backup repository"
        },
        "commitMessage": "=Back up all workflows"
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1.1,
      "waitBetweenTries": 2000
    },
    {
      "id": "c022de14-3904-4f1d-9776-a6de11afbd6a",
      "name": "Convert Single Cred to JSON",
      "type": "n8n-nodes-base.convertToFile",
      "position": [
        6848,
        2080
      ],
      "parameters": {
        "mode": "each",
        "options": {
          "fileName": "={{ $json.name }}_{{ $json.type }}.json"
        },
        "operation": "toJson"
      },
      "typeVersion": 1.1
    },
    {
      "id": "338e97d9-2478-4ac1-8b9d-ba7acc6076b2",
      "name": "Prep All Creds Payload",
      "type": "n8n-nodes-base.code",
      "position": [
        6848,
        2512
      ],
      "parameters": {
        "jsCode": "const path = $(\"Init Creds Folder\").first().json.content.path;\nconst folder = path.split('/').slice(0, -2).join('/');\n\nreturn $input.all().map(item => ({\n  json: {\n    ...item.json,\n    path,\n    folder\n  },\n  binary: item.binary\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "3c23ce8d-7d29-4553-a06f-2a466fbb8847",
      "name": "All Creds to JSON",
      "type": "n8n-nodes-base.convertToFile",
      "position": [
        6624,
        2512
      ],
      "parameters": {
        "options": {
          "fileName": "credentials-all.json"
        },
        "operation": "toJson"
      },
      "typeVersion": 1.1
    },
    {
      "id": "48abda3d-d255-48b8-be2b-ac7cc37d22db",
      "name": "Push All Creds",
      "type": "n8n-nodes-base.github",
      "maxTries": 3,
      "position": [
        7072,
        2512
      ],
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $(\"Backup Configuration\").first().json.githubOwner }}",
          "cachedResultName": "Configured GitHub owner"
        },
        "filePath": "={{ $json.folder + '/' + $binary.data.fileName }}",
        "resource": "file",
        "binaryData": true,
        "repository": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $(\"Backup Configuration\").first().json.githubRepository }}",
          "cachedResultName": "Configured backup repository"
        },
        "commitMessage": "=Back up all encrypted credentials"
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1.1,
      "waitBetweenTries": 2000
    },
    {
      "id": "c347a990-6542-478c-9829-41794ccf26a5",
      "name": "Merge WFs & Creds",
      "type": "n8n-nodes-base.merge",
      "position": [
        7712,
        1776
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition"
      },
      "typeVersion": 3.2
    },
    {
      "id": "e0f007f7-499a-4a4e-8afe-496a06368394",
      "name": "Convert Combined Data",
      "type": "n8n-nodes-base.convertToFile",
      "position": [
        8128,
        1776
      ],
      "parameters": {
        "mode": "each",
        "options": {
          "fileName": "=workflows-credentials.json"
        },
        "operation": "toJson"
      },
      "typeVersion": 1.1
    },
    {
      "id": "802d67aa-7d6a-4ae0-b823-d2c9a1b550ad",
      "name": "Prep Combined Payload",
      "type": "n8n-nodes-base.code",
      "position": [
        8352,
        1776
      ],
      "parameters": {
        "jsCode": "const path = $(\"Init Creds Folder\").first().json.content.path;\nconst folder = path.split('/').slice(0, -1).join('/');\n\nconst incomingBinary = items[0]?.binary || {};\n\nreturn [{\n  json: {\n    path,\n    folder,\n  },\n  binary: incomingBinary \n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "e9c6e929-6b16-49c4-8b76-d7c1e3bcb649",
      "name": "Push Combined File",
      "type": "n8n-nodes-base.github",
      "maxTries": 3,
      "position": [
        8608,
        1776
      ],
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $(\"Backup Configuration\").first().json.githubOwner }}",
          "cachedResultName": "Configured GitHub owner"
        },
        "filePath": "={{ $json.folder.split('/').slice(0, -1).join('/') + '/' + $binary.data.fileName }}",
        "resource": "file",
        "binaryData": true,
        "repository": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $(\"Backup Configuration\").first().json.githubRepository }}",
          "cachedResultName": "Configured backup repository"
        },
        "commitMessage": "=Back up combined workflows and encrypted credentials"
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1.1,
      "waitBetweenTries": 2000
    },
    {
      "id": "6ecda0aa-2a8b-4a6e-ab88-33897408d525",
      "name": "Extract Binary Data",
      "type": "n8n-nodes-base.code",
      "position": [
        5344,
        3328
      ],
      "parameters": {
        "jsCode": "const item = $input.first();\n\nconst output = [];\n\nfor (const [key, binary] of Object.entries(item.binary || {})) {\n  output.push({\n    json: {\n      field: key\n    },\n    binary: {\n      data: binary\n    }\n  });\n}\n\nreturn output;"
      },
      "typeVersion": 2
    },
    {
      "id": "208fc7da-816b-4acd-b6c7-b3691d115dd2",
      "name": "Extract JSON from Binary",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        5568,
        3328
      ],
      "parameters": {
        "options": {},
        "operation": "fromJson"
      },
      "typeVersion": 1.1
    },
    {
      "id": "c11415df-d2bf-450f-8cd2-a17c2b4324eb",
      "name": "Route Import Data",
      "type": "n8n-nodes-base.code",
      "position": [
        5792,
        3328
      ],
      "parameters": {
        "jsCode": "const output = [];\n\nfor (const item of $input.all()) {\n\n  const data = item.json.data;\n\n  // Single workflow\n  if (data?.nodes) {\n    output.push({\n      json: {\n        type: 'workflows',\n        data: [data]\n      }\n    });\n    continue;\n  }\n\n  // Single credential\n  if (data?.type && data?.id) {\n    output.push({\n      json: {\n        type: 'credentials',\n        data: [data]\n      }\n    });\n    continue;\n  }\n\n  // workflows export all\n  if (Array.isArray(data) && data.length && data[0]?.nodes) {\n    output.push({\n      json: {\n        type: 'workflows',\n        data\n      }\n    });\n    continue;\n  }\n\n  // credentials export all\n  if (Array.isArray(data) && data.length && data[0]?.type) {\n    output.push({\n      json: {\n        type: 'credentials',\n        data\n      }\n    });\n    continue;\n  }\n\n  // backup all\n  if (data?.workflows || data?.credentials) {\n\n    if (data.workflows) {\n      output.push({\n        json: {\n          type: 'workflows',\n          data: data.workflows\n        }\n      });\n    }\n\n    if (data.credentials) {\n      output.push({\n        json: {\n          type: 'credentials',\n          data: data.credentials\n        }\n      });\n    }\n  }\n}\n\nreturn output;"
      },
      "typeVersion": 2
    },
    {
      "id": "ffb6fc19-cc74-4d77-984b-d25a7d167d9b",
      "name": "Workflows vs Creds",
      "type": "n8n-nodes-base.switch",
      "position": [
        6016,
        3328
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "4c4ed0eb-1f95-4257-a1cc-316f4a98400f",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.type }}",
                    "rightValue": "workflows"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "e3d5456c-68e8-471b-b4ae-7a594b18c73f",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.type }}",
                    "rightValue": "credentials"
                  }
                ]
              }
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.4
    },
    {
      "id": "58ae2535-06bc-4a2f-bd95-405df45b5f6a",
      "name": "Split WF Items",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        6272,
        3168
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "data"
      },
      "typeVersion": 1
    },
    {
      "id": "872dc7ce-96ab-4c59-ba30-7b240dad2059",
      "name": "Split Cred Items",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        6272,
        3568
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "data"
      },
      "typeVersion": 1
    },
    {
      "id": "b89005ef-7733-49fd-a0fc-d958ee24041f",
      "name": "Convert WF for Import",
      "type": "n8n-nodes-base.convertToFile",
      "position": [
        6496,
        3168
      ],
      "parameters": {
        "options": {
          "fileName": "workflows.json"
        },
        "operation": "toJson"
      },
      "typeVersion": 1.1
    },
    {
      "id": "7ff068ad-ebb2-4a0e-8b34-2f0c393b4934",
      "name": "Convert Cred for Import",
      "type": "n8n-nodes-base.convertToFile",
      "position": [
        6496,
        3568
      ],
      "parameters": {
        "options": {
          "fileName": "credentials.json"
        },
        "operation": "toJson"
      },
      "typeVersion": 1.1
    },
    {
      "id": "aeca47f7-acb6-45b6-a24b-aff780f534b8",
      "name": "Upload WFs to /tmp",
      "type": "n8n-nodes-base.ssh",
      "maxTries": 2,
      "position": [
        6720,
        3168
      ],
      "parameters": {
        "path": "=/tmp/workflows-{{ $now.format('dd-MM-yyyy') }}/workflows.json",
        "options": {},
        "resource": "file"
      },
      "credentials": {
        "sshPassword": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1,
      "waitBetweenTries": 1500
    },
    {
      "id": "8edec23b-eb02-4c9d-ba15-72cce6b5c9f6",
      "name": "Exec Import Workflow",
      "type": "n8n-nodes-base.ssh",
      "maxTries": 2,
      "position": [
        6944,
        3168
      ],
      "parameters": {
        "command": "=docker cp \"/tmp/workflows-{{ $now.format('dd-MM-yyyy') }}/{{ $binary.data.fileName }}\" {{ $(\"Restore Configuration\").first().json.dockerContainer }}:/tmp/ && docker exec {{ $(\"Restore Configuration\").first().json.dockerContainer }} sh -c \"n8n import:workflow --input=/tmp/workflows.json\""
      },
      "credentials": {
        "sshPassword": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1,
      "waitBetweenTries": 1500
    },
    {
      "id": "184380fd-812c-4bef-8df9-33ee1ffc99a3",
      "name": "Exec Import Credential",
      "type": "n8n-nodes-base.ssh",
      "maxTries": 2,
      "position": [
        6944,
        3568
      ],
      "parameters": {
        "command": "=docker cp \"/tmp/credentials-{{ $now.format('dd-MM-yyyy') }}/{{ $binary.data.fileName }}\" {{ $(\"Restore Configuration\").first().json.dockerContainer }}:/tmp/ && docker exec {{ $(\"Restore Configuration\").first().json.dockerContainer }} sh -c \"n8n import:credentials --input=/tmp/credentials.json\""
      },
      "credentials": {
        "sshPassword": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1,
      "waitBetweenTries": 1500
    },
    {
      "id": "16d94919-afb9-4525-bd95-d5782015591d",
      "name": "Upload Creds to /tmp",
      "type": "n8n-nodes-base.ssh",
      "maxTries": 2,
      "position": [
        6720,
        3568
      ],
      "parameters": {
        "path": "=/tmp/credentials-{{ $now.format('dd-MM-yyyy') }}/credentials.json",
        "options": {},
        "resource": "file"
      },
      "credentials": {
        "sshPassword": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 1,
      "waitBetweenTries": 1500
    },
    {
      "id": "6b0752a4-3d54-4126-924b-fde671b75ed6",
      "name": "Backup Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        5136,
        1696
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "a096a8a6-de33-4a7e-b2f0-e4b4e62ce132",
              "name": "githubOwner",
              "type": "string",
              "value": "REPLACE PROFILE GITHUB NAME"
            },
            {
              "id": "106075bf-a60c-4382-90aa-e3cb6038ae76",
              "name": "githubRepository",
              "type": "string",
              "value": "REPLACE REPO GITHUB NAME"
            },
            {
              "id": "66ef4008-3294-4727-b5c1-575fa44c4d39",
              "name": "dockerContainer",
              "type": "string",
              "value": "REPLACE CONTAINER N8N NAME"
            },
            {
              "id": "ea4e4e60-1124-4ec6-8bf1-44a06ab1884f",
              "name": "date",
              "type": "string",
              "value": "={{ $now.format('dd-MM-yyyy HH-mm-ss') }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "fb9cbe58-0d16-4761-8386-35a8785059eb",
      "name": "Restore Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        5120,
        3328
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "07149403-9ed6-4747-a060-8974a6e60f6a",
              "name": "dockerContainer",
              "type": "string",
              "value": "REPLACE CONTAINER N8N NAME"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "0ea2922b-98a5-406f-941d-350578da67c6",
      "name": "00 - Template overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4688,
        912
      ],
      "parameters": {
        "color": 5,
        "width": 4160,
        "height": 1824,
        "content": "# Back up and restore n8n on Docker\n\n## For **self-hosted n8n running in Docker on a Linux server**.\n\n## The workflow has two independent paths:\n### 1. Scheduled backup to a private GitHub repository\n### 2. Authenticated restore from the generated JSON files\n\n## SSH is used instead of Execute Command so n8n can connect to the Linux host and run the n8n CLI inside the Docker container.\n\n## Follow the numbered notes from left to right. Configure credentials and the two configuration nodes before activating the workflow."
      },
      "typeVersion": 1
    },
    {
      "id": "157c04f0-42c0-4756-878c-67738bb1bf44",
      "name": "Upload Restore File",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        4896,
        3328
      ],
      "parameters": {
        "options": {
          "customCss": ":root {\n\t--font-family: 'Open Sans', sans-serif;\n\t--font-weight-normal: 400;\n\t--font-weight-bold: 600;\n\t--font-size-body: 12px;\n\t--font-size-label: 14px;\n\t--font-size-test-notice: 12px;\n\t--font-size-input: 14px;\n\t--font-size-header: 20px;\n\t--font-size-paragraph: 14px;\n\t--font-size-link: 12px;\n\t--font-size-error: 12px;\n\t--font-size-html-h1: 28px;\n\t--font-size-html-h2: 20px;\n\t--font-size-html-h3: 16px;\n\t--font-size-html-h4: 14px;\n\t--font-size-html-h5: 12px;\n\t--font-size-html-h6: 10px;\n\t--font-size-subheader: 14px;\n\n\t/* Colors */\n\t--color-background: #fbfcfe;\n\t--color-test-notice-text: #e6a23d;\n\t--color-test-notice-bg: #fefaf6;\n\t--color-test-notice-border: #f6dcb7;\n\t--color-card-bg: #ffffff;\n\t--color-card-border: #dbdfe7;\n\t--color-card-shadow: rgba(99, 77, 255, 0.06);\n\t--color-link: #7e8186;\n\t--color-header: #525356;\n\t--color-label: #555555;\n\t--color-input-border: #dbdfe7;\n\t--color-input-text: #71747A;\n\t--color-focus-border: rgb(90, 76, 194);\n\t--color-submit-btn-bg: #ff6d5a;\n\t--color-submit-btn-text: #ffffff;\n\t--color-error: #ea1f30;\n\t--color-required: #ff6d5a;\n\t--color-clear-button-bg: #7e8186;\n\t--color-html-text: #555;\n\t--color-html-link: #ff6d5a;\n\t--color-header-subtext: #7e8186;\n\n\t/* Border Radii */\n\t--border-radius-card: 8px;\n\t--border-radius-input: 6px;\n\t--border-radius-clear-btn: 50%;\n\t--card-border-radius: 8px;\n\n\t/* Spacing */\n\t--padding-container-top: 24px;\n\t--padding-card: 24px;\n\t--padding-test-notice-vertical: 12px;\n\t--padding-test-notice-horizontal: 24px;\n\t--margin-bottom-card: 16px;\n\t--padding-form-input: 12px;\n\t--card-padding: 24px;\n\t--card-margin-bottom: 16px;\n\n\t/* Dimensions */\n\t--container-width: 448px;\n\t--submit-btn-height: 48px;\n\t--checkbox-size: 18px;\n\n\t/* Others */\n\t--box-shadow-card: 0px 4px 16px 0px var(--color-card-shadow);\n\t--opacity-placeholder: 0.5;\n}\n\n.form-header p {\n  text-align: left;\n}",
          "appendAttribution": false
        },
        "formTitle": "Restore n8n workflows and credentials",
        "formFields": {
          "values": [
            {
              "fieldType": "file",
              "fieldLabel": "data",
              "requiredField": true,
              "acceptFileTypes": ".json"
            }
          ]
        },
        "responseMode": "lastNode",
        "authentication": "basicAuth",
        "formDescription": "Upload JSON files created by this template. Matching workflow or credential\nIDs can be overwritten. Use only on a trusted self-hosted n8n instance.\nBasic Auth must be configured before activation."
      },
      "credentials": {
        "httpBasicAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.5
    },
    {
      "id": "0dac9ffd-64cc-459b-82e3-6767934d4589",
      "name": "Format Combined Data",
      "type": "n8n-nodes-base.code",
      "position": [
        7920,
        1776
      ],
      "parameters": {
        "jsCode": "// 1. L?y d? li?u t? node (d\ufffdng optional chaining fallback v? m?ng r?ng n?u kh\ufffdng c\ufffd data)\nconst workflowsData = $(\"Parse Workflow Export\").first()?.json?.workflows || [];\nconst credsData = $(\"Parse Creds Export\").first()?.json?.credentials || []; // D?i key \"credentials\" n?u node kia d\ufffdng t\ufffdn kh\ufffdc\n\n// 2. Return data d\ufffd g?p\nreturn [{\n  json: {\n    workflows: workflowsData,\n    credentials: credsData\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "012c769a-c27d-4d53-a07d-f72b293c26a9",
      "name": "01 - Schedule and configuration",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4736,
        1392
      ],
      "parameters": {
        "color": 3,
        "width": 704,
        "height": 592,
        "content": "## Step 1 - Schedule and backup configuration\n\n**Input:** Schedule Trigger\n\nThis group:\n- Generates a timestamp for the backup folder\n- Defines GitHub owner and repository\n- Defines the Docker container name\n\nEdit only **Backup Configuration** when adapting this template. Keep the\nworkflow inactive until GitHub and SSH credentials are configured."
      },
      "typeVersion": 1
    },
    {
      "id": "056ea806-d095-40c1-b109-72f4fc1a9cfc",
      "name": "02 - Initialize GitHub folders",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5440,
        1312
      ],
      "parameters": {
        "color": 6,
        "width": 448,
        "height": 880,
        "content": "## Step 2 - Initialize backup folders\n\nCreates timestamped placeholder files so GitHub has:\n- `/workflows`\n- `/credentials`\n\nThe returned GitHub paths are reused by later nodes. Both nodes use the\nrepository selected in **Backup Configuration**."
      },
      "typeVersion": 1
    },
    {
      "id": "c8ddf3a7-b8a7-4249-928a-189e97ac3a45",
      "name": "03 - Export workflows",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5888,
        1312
      ],
      "parameters": {
        "color": 4,
        "width": 608,
        "height": 416,
        "content": "## Step 3A - Export workflows over SSH\n\nRuns `n8n export:workflow --all` inside the configured Docker container.\n\nThen:\n1. Parses CLI stdout as JSON\n2. Splits the workflow array into individual items\n\nThe output feeds both the all-workflows backup and the per-workflow loop."
      },
      "typeVersion": 1
    },
    {
      "id": "c28f4815-693a-465c-aa85-d328e0e79e00",
      "name": "04 - Export credentials",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5888,
        1776
      ],
      "parameters": {
        "color": 4,
        "width": 608,
        "height": 416,
        "content": "## Step 3B - Export encrypted credentials\n\nRuns `n8n export:credentials --all` inside the Docker container.\n\nThen:\n1. Parses CLI stdout as JSON\n2. Splits the credential array into individual items\n\nCredentials remain encrypted. Restore requires the same\n`N8N_ENCRYPTION_KEY`."
      },
      "typeVersion": 1
    },
    {
      "id": "90ae8e78-b42d-44bd-99de-598d4dde3532",
      "name": "05 - Back up all workflows",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6560,
        944
      ],
      "parameters": {
        "color": 6,
        "width": 880,
        "height": 352,
        "content": "## Step 4A - Save all workflows in one file\n\nConverts the complete workflow export to `workflows-all.json`, prepares the\ntarget GitHub path, and commits the file.\n\nUse this file when restoring all workflows together."
      },
      "typeVersion": 1
    },
    {
      "id": "4f4b29c7-d71f-4803-b139-c5aa20f79fc5",
      "name": "06 - Loop through each workflow",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6560,
        1328
      ],
      "parameters": {
        "color": 6,
        "width": 880,
        "height": 480,
        "content": "## Step 4B - Back up each workflow separately\n\nThe loop processes workflows in batches:\n1. Take the next workflow item\n2. Convert it to `<workflow name>.json`\n3. Prepare its GitHub folder path\n4. Commit it to the private repository\n5. Return to **Loop: Single WF**\n\nIndividual files make reviewing and restoring one workflow easier."
      },
      "typeVersion": 1
    },
    {
      "id": "004234a8-0627-422c-8845-85ea9e3c5d21",
      "name": "07 - Loop through each credential",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6560,
        1872
      ],
      "parameters": {
        "color": 6,
        "width": 880,
        "height": 480,
        "content": "## Step 4C - Back up each credential separately\n\nThe loop processes encrypted credential records in batches:\n1. Take the next credential item\n2. Convert it to `<name>_<type>.json`\n3. Prepare its GitHub folder path\n4. Commit it to the private repository\n5. Return to **Loop: Single Cred**\n\nNever publish these files or use a public repository."
      },
      "typeVersion": 1
    },
    {
      "id": "d8672c0e-705a-4788-9b19-5a1c8c799f63",
      "name": "08 - Back up all credentials",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6560,
        2352
      ],
      "parameters": {
        "color": 6,
        "width": 880,
        "height": 320,
        "content": "## Step 4D - Save all credentials in one file\n\nConverts the complete encrypted credential export to\n`credentials-all.json`, prepares the target path, and commits it.\n\nThis file is useful for a full-instance restore with the same encryption key."
      },
      "typeVersion": 1
    },
    {
      "id": "35b8706f-00ec-44f8-afde-393ec4d0985e",
      "name": "09 - Create combined backup",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        7632,
        1472
      ],
      "parameters": {
        "color": 2,
        "width": 1168,
        "height": 544,
        "content": "## Step 5 - Create one portable backup package\n\nWaits for the all-workflows and all-credentials branches, combines the parsed\ndata, and creates `workflows-credentials.json`.\n\nThe final file contains:\n- `workflows`\n- encrypted `credentials`\n\nIt is committed at the root of the timestamped backup folder and can be\nuploaded directly to the restore form."
      },
      "typeVersion": 1
    },
    {
      "id": "01a9e1d1-a301-4871-b830-35c304f3b096",
      "name": "10 - Upload restore files",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4720,
        3152
      ],
      "parameters": {
        "color": 3,
        "width": 576,
        "height": 480,
        "content": "## Step 6 - Authenticated restore input\n\n**Upload Restore File** accepts one or more `.json` backup files.\n\n**Restore Configuration** defines the destination Docker container and keeps\nuploaded binary data available to the next node.\n\nProtect the form with Basic Auth and HTTPS. Never expose it anonymously."
      },
      "typeVersion": 1
    },
    {
      "id": "62edac24-ed96-4c60-80cd-4def880a710a",
      "name": "11 - Detect backup file type",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5296,
        3152
      ],
      "parameters": {
        "color": 4,
        "width": 960,
        "height": 480,
        "content": "## Step 7 - Read, validate, and route JSON\n\nThis group:\n1. Extracts every uploaded binary file\n2. Parses JSON\n3. Detects workflow, credential, array, or combined backup formats\n4. Routes workflow and credential records to separate outputs\n\nOnly upload trusted files. Imported workflows may contain executable nodes."
      },
      "typeVersion": 1
    },
    {
      "id": "c29045e9-af10-4349-929b-036c0b60c0a4",
      "name": "12 - Import workflows",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6256,
        2928
      ],
      "parameters": {
        "color": 5,
        "width": 864,
        "height": 432,
        "content": "## Step 8A - Restore workflows over SSH\n\nFor each workflow record:\n1. Build `workflows.json`\n2. Upload it to a temporary Linux host directory\n3. Copy it into the n8n Docker container\n4. Run `n8n import:workflow`\n\nMatching IDs may be overwritten. Imported workflows are deactivated and must\nbe reviewed before activation."
      },
      "typeVersion": 1
    },
    {
      "id": "64022511-1642-4f7d-b883-8b99be0e3cd7",
      "name": "13 - Import credentials",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6256,
        3360
      ],
      "parameters": {
        "color": 5,
        "width": 864,
        "height": 432,
        "content": "## Step 8B - Restore credentials over SSH\n\nFor each credential record:\n1. Build `credentials.json`\n2. Upload it to a temporary Linux host directory\n3. Copy it into the n8n Docker container\n4. Run `n8n import:credentials`\n\nThe destination must use the same `N8N_ENCRYPTION_KEY` as the source."
      },
      "typeVersion": 1
    },
    {
      "id": "6d23392c-63f0-4648-8d3b-7f19465fcffb",
      "name": "14 - Security checklist",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        7120,
        3088
      ],
      "parameters": {
        "color": 2,
        "width": 896,
        "height": 608,
        "content": "## Before production use\n\n- Use a private GitHub repository\n- Prefer SSH private-key authentication\n- Restrict the SSH user to the required Docker operations\n- Store `N8N_ENCRYPTION_KEY` in a separate secret manager\n- Protect the restore form with Basic Auth and HTTPS\n- Test restore on staging\n- Review imported workflows before activation\n- Keep an additional backup outside GitHub"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "2eea4fc5-8a80-45f5-9110-62d24a57d7d3",
  "nodeGroups": [],
  "connections": {
    "Push All WFs": {
      "main": [
        [
          {
            "node": "Merge WFs & Creds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Push All Creds": {
      "main": [
        [
          {
            "node": "Merge WFs & Creds",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Push Single WF": {
      "main": [
        [
          {
            "node": "Loop: Single WF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split WF Items": {
      "main": [
        [
          {
            "node": "Convert WF for Import",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Trigger Backup": {
      "main": [
        [
          {
            "node": "Backup Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop: Single WF": {
      "main": [
        [],
        [
          {
            "node": "Convert Single WF to JSON",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Workflows": {
      "main": [
        [
          {
            "node": "Convert All WFs to JSON",
            "type": "main",
            "index": 0
          },
          {
            "node": "Loop: Single WF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Export Workflows": {
      "main": [
        [
          {
            "node": "Parse Workflow Export",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Push Single Cred": {
      "main": [
        [
          {
            "node": "Loop: Single Cred",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Cred Items": {
      "main": [
        [
          {
            "node": "Convert Cred for Import",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "All Creds to JSON": {
      "main": [
        [
          {
            "node": "Prep All Creds Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Init Creds Folder": {
      "main": [
        [
          {
            "node": "Export Credentials",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop: Single Cred": {
      "main": [
        [],
        [
          {
            "node": "Convert Single Cred to JSON",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge WFs & Creds": {
      "main": [
        [
          {
            "node": "Format Combined Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route Import Data": {
      "main": [
        [
          {
            "node": "Workflows vs Creds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Credentials": {
      "main": [
        [
          {
            "node": "Loop: Single Cred",
            "type": "main",
            "index": 0
          },
          {
            "node": "All Creds to JSON",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Export Credentials": {
      "main": [
        [
          {
            "node": "Parse Creds Export",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Creds Export": {
      "main": [
        [
          {
            "node": "Split Credentials",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload WFs to /tmp": {
      "main": [
        [
          {
            "node": "Exec Import Workflow",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Workflows vs Creds": {
      "main": [
        [
          {
            "node": "Split WF Items",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Split Cred Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Binary Data": {
      "main": [
        [
          {
            "node": "Extract JSON from Binary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Restore File": {
      "main": [
        [
          {
            "node": "Restore Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Backup Configuration": {
      "main": [
        [
          {
            "node": "Init Workflow Folder",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Combined Data": {
      "main": [
        [
          {
            "node": "Convert Combined Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Init Workflow Folder": {
      "main": [
        [
          {
            "node": "Init Creds Folder",
            "type": "main",
            "index": 0
          },
          {
            "node": "Export Workflows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prep All WFs Payload": {
      "main": [
        [
          {
            "node": "Push All WFs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Creds to /tmp": {
      "main": [
        [
          {
            "node": "Exec Import Credential",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convert Combined Data": {
      "main": [
        [
          {
            "node": "Prep Combined Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convert WF for Import": {
      "main": [
        [
          {
            "node": "Upload WFs to /tmp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Workflow Export": {
      "main": [
        [
          {
            "node": "Split Workflows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prep Combined Payload": {
      "main": [
        [
          {
            "node": "Push Combined File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Restore Configuration": {
      "main": [
        [
          {
            "node": "Extract Binary Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prep All Creds Payload": {
      "main": [
        [
          {
            "node": "Push All Creds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prep Single WF Payload": {
      "main": [
        [
          {
            "node": "Push Single WF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convert All WFs to JSON": {
      "main": [
        [
          {
            "node": "Prep All WFs Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convert Cred for Import": {
      "main": [
        [
          {
            "node": "Upload Creds to /tmp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract JSON from Binary": {
      "main": [
        [
          {
            "node": "Route Import Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prep Single Cred Payload": {
      "main": [
        [
          {
            "node": "Push Single Cred",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convert Single WF to JSON": {
      "main": [
        [
          {
            "node": "Prep Single WF Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convert Single Cred to JSON": {
      "main": [
        [
          {
            "node": "Prep Single Cred Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}