Best way to save and import credentials

Hello !

Congrat’s for this great project !

Is the only way of saving credentials making a copy of ~/.n8n/config ? And then paste it back if we want to import them ?

https://docs.n8n.io/#/node-basics?id=credentials

What are the best practices in terms of saving and importing a whole project ? (In my case : when I update docker image it erases all workflows, so I reimport them manually…)

Thanks !

Hey @alexsonnay. First of all, welcome to the community! We hope that you enjoy your time here!

The question that you have is a very important one. What do you do with all your hard work creating integrations and flows, configuring your environment, etc. when you have to abandon the n8n setup you already have?

As a part of a set of management scripts that I am building for n8n, I am going to be looking at how to take all of this work, make a backup copy of it and move it somewhere else so that it can be imported again into a new environment. This way, you have a quick and easy way to get back up and running again as quickly as possible.

As an experiment (that means “I just hacked this together and have not really tested it very well but it may be enough to get you started but if something goes wrong, don’t blame me!” :slight_smile: ), I put together these two flows. The first one (Credential Backup) will push your config file and database to Dropbox for a quick backup.

The second one (Credential Recovery) will grab your config and database files from Dropbox and replace them.

You will have to tweak each flow for your environment and configure Dropbox and its credentials. Be aware that for this to work, you will have to manually add in your Dropbox credentials before it can run and get your other information.

{
  "name": "Credential Backup",
  "nodes": [
    {
      "parameters": {
        "keepOnlySet": true,
        "values": {
          "string": [
            {
              "name": "database",
              "value": "/home/n8n/.n8n/database.sqlite"
            },
            {
              "name": "config",
              "value": "/home/n8n/.n8n/config"
            }
          ]
        },
        "options": {}
      },
      "name": "Set",
      "type": "n8n-nodes-base.set",
      "typeVersion": 1,
      "position": [
        460,
        200
      ],
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "path": "={{$node[\"Set\"].parameter[\"values\"][\"string\"][1][\"value\"]}}"
      },
      "name": "Upload Config",
      "type": "n8n-nodes-base.dropbox",
      "typeVersion": 1,
      "position": [
        730,
        310
      ],
      "credentials": {
        "dropboxApi": "Tephlon's Dropbox"
      },
      "notes": "Upload config"
    },
    {
      "parameters": {
        "path": "={{$node[\"Set\"].parameter[\"values\"][\"string\"][0][\"value\"]}}"
      },
      "name": "Upload DB",
      "type": "n8n-nodes-base.dropbox",
      "typeVersion": 1,
      "position": [
        730,
        120
      ],
      "credentials": {
        "dropboxApi": "Tephlon's Dropbox"
      },
      "notes": "Upload database"
    },
    {
      "parameters": {},
      "name": "Start Backup",
      "type": "n8n-nodes-base.start",
      "typeVersion": 1,
      "position": [
        260,
        200
      ]
    }
  ],
  "connections": {
    "Set": {
      "main": [
        [
          {
            "node": "Upload DB",
            "type": "main",
            "index": 0
          },
          {
            "node": "Upload Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Start Backup": {
      "main": [
        [
          {
            "node": "Set",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {},
  "id": "1"
}
{
  "name": "Credential Recovery",
  "nodes": [
    {
      "parameters": {
        "keepOnlySet": true,
        "values": {
          "string": [
            {
              "name": "database",
              "value": "/home/n8n/.n8n/database.sqlite"
            },
            {
              "name": "config",
              "value": "/home/n8n/.n8n/config"
            }
          ]
        },
        "options": {}
      },
      "name": "Set",
      "type": "n8n-nodes-base.set",
      "typeVersion": 1,
      "position": [
        460,
        200
      ],
      "alwaysOutputData": true
    },
    {
      "parameters": {},
      "name": "Start Recovery",
      "type": "n8n-nodes-base.start",
      "typeVersion": 1,
      "position": [
        260,
        200
      ]
    },
    {
      "parameters": {
        "operation": "download",
        "path": "={{$node[\"Set\"].parameter[\"values\"][\"string\"][0][\"value\"]}}"
      },
      "name": "Recover DB",
      "type": "n8n-nodes-base.dropbox",
      "typeVersion": 1,
      "position": [
        730,
        120
      ],
      "credentials": {
        "dropboxApi": "Tephlon's Dropbox"
      },
      "notes": "Recover database"
    },
    {
      "parameters": {
        "operation": "download",
        "path": "={{$node[\"Set\"].parameter[\"values\"][\"string\"][1][\"value\"]}}"
      },
      "name": "Recover Config",
      "type": "n8n-nodes-base.dropbox",
      "typeVersion": 1,
      "position": [
        730,
        310
      ],
      "credentials": {
        "dropboxApi": "Tephlon's Dropbox"
      },
      "notes": "Recover config"
    }
  ],
  "connections": {
    "Set": {
      "main": [
        [
          {
            "node": "Recover DB",
            "type": "main",
            "index": 0
          },
          {
            "node": "Recover Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Start Recovery": {
      "main": [
        [
          {
            "node": "Set",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {},
  "id": "2"
}
2 Likes

Thanks a lot @Tephlon! Yes, what you described is probably the easiest way to simply copy the database file.

In the future we want to offer an easier way and also make it possible via an official API or via CLI. Right now that does sadly not exist yet. What you could however use is the API which the Editor-UI uses. You could so theoretically build a workflow which queries all the data you need with a simple HTTP Request node to this endpoint:

Something similar would then also be possible to save the data back or to backup the workflows.

Hope that is helpful!

2 Likes

Thanks for your snippets ! Great idea.

However I don’t think the path is right. I ran my container and it seems to be

"/home/node/.n8n/database.sqlite".

But in both cases I get an error unfortunately :

Error: File not found: /home/n8n/.n8n/database.sqlite.
    at Gaxios._request (/usr/local/lib/node_modules/n8n/node_modules/gaxios/build/src/gaxios.js:85:23)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at async JWT.requestAsync (/usr/local/lib/node_modules/n8n/node_modules/google-auth-library/build/src/auth/oauth2client.js:340:18)
    at async Object.execute (/usr/local/lib/node_modules/n8n/node_modules/n8n-nodes-base/dist/nodes/Google/GoogleDrive.node.js:921:38)
    at async /usr/local/lib/node_modules/n8n/node_modules/n8n-core/dist/src/WorkflowExecute.js:370:47

The location of the database and configuration files may be different for your setup. If you are using Linux, it should be at ~/.n8n/database.sqlite. ~ represents your home folder. So, in the flow that I have put together, the home folder (~) is /home/n8n. You will have to change that portion of the flow with your home folder. That should get you on to the next step.

It’s working with Dropbox ! :rocket:

NOT with Google Drive !

:warning: there isn’t any “overwrite” option in the Dropbox module ==> the file stored in Dropbox will hence remain the initial one forever.

To solve that, use HTTP Request module instead to add the “overwrite” mode.

1 Like

I just checked the workflow very fast. It seems like that “Credential Backup” workflow had an error in it. It used the Dropbox destination path as read path. What is however needed is to read the files with the “Read File” node and then this data has to be used by the Dropbox node like in this example:

{
  "nodes": [
    {
      "parameters": {
        "keepOnlySet": true,
        "values": {
          "string": [
            {
              "name": "database",
              "value": "/home/n8n/.n8n/database.sqlite"
            },
            {
              "name": "config",
              "value": "/home/n8n/.n8n/config"
            }
          ]
        },
        "options": {}
      },
      "name": "Set",
      "type": "n8n-nodes-base.set",
      "typeVersion": 1,
      "position": [
        500,
        300
      ],
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "path": "=/n8n-backup/{{$node[\"Read DB File1\"].binary.data.fileName}}",
        "binaryData": true
      },
      "name": "Upload Config",
      "type": "n8n-nodes-base.dropbox",
      "typeVersion": 1,
      "position": [
        950,
        450
      ],
      "credentials": {
        "dropboxApi": ""
      },
      "notes": "Upload config"
    },
    {
      "parameters": {
        "path": "=/n8n-backup/{{$node[\"Read DB File\"].binary.data.fileName}}",
        "binaryData": true
      },
      "name": "Upload DB",
      "type": "n8n-nodes-base.dropbox",
      "typeVersion": 1,
      "position": [
        950,
        200
      ],
      "credentials": {
        "dropboxApi": ""
      },
      "notes": "Upload config"
    },
    {
      "parameters": {
        "filePath": "={{$node[\"Set\"].json[\"database\"]}}"
      },
      "name": "Read DB File",
      "type": "n8n-nodes-base.readBinaryFile",
      "typeVersion": 1,
      "position": [
        750,
        200
      ]
    },
    {
      "parameters": {
        "filePath": "={{$node[\"Set\"].json[\"config\"]}}"
      },
      "name": "Read Config File",
      "type": "n8n-nodes-base.readBinaryFile",
      "typeVersion": 1,
      "position": [
        750,
        450
      ]
    }
  ],
  "connections": {
    "Set": {
      "main": [
        [
          {
            "node": "Read DB File",
            "type": "main",
            "index": 0
          },
          {
            "node": "Read Config File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read DB File": {
      "main": [
        [
          {
            "node": "Upload DB",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Config File": {
      "main": [
        [
          {
            "node": "Upload Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

For the recovery workflow, it would be the same. It would have to use the data from the Dropbox Node and write it with the help of the “Write Binary File” Node.

2 Likes

Nice !

However, I tried the “Credentials Recovery” workflow and it does not work. Nothing happens as when you use “Download” from Dropbox it will not replace the sqlite and the config files inside the Docker image…

Ah yes, like written above would also the recovery workflow have to be updated. But honestly not 100% sure both workflow work perfectly as both times n8n is actually still using the database so not sure if it would mess something up.

I can confirm because I lost all my workflows :frowning:

Tried to push back directly the SQLite + config into docker container but nothing is loaded in the front.

Hm, in this case it is then probably better to use the proper commands as described here:

1 Like

Sqlite database has credentials configuration ? If no, how can we backup credentials config and load them ?