n8nのワークフローと認証情報をCLIでバックアップし、別環境へ移行する

公開:

本記事には広告(PR)が含まれます。

セルフホストのn8nで、障害復旧や環境移行に備えたバックアップ手順を整備したい人向けの記事。ワークフローと認証情報のバックアップから別環境への復元までの一連の流れを、Docker上のn8n 2.35.4で実測した。ボリュームやDBファイルごと退避する方法は扱わず、CLIのエクスポートとインポートに絞る。

結論

n8nのワークフローのバックアップはn8n export:workflow --backup --output=<ディレクトリ>、復元はn8n import:workflow --separate --input=<ディレクトリ>で完結する。認証情報まで移行する場合は、復元先を移行元と同じN8N_ENCRYPTION_KEYで起動しないと復号できない。

やったことと結果は次のとおり。

やったこと結果
ワークフロー2件をexport:workflow --backupでバックアップ成功。所要時間4.51秒(1回計測)、個別ファイル計5,291バイト(2,745+2,546)
別コンテナへimport:workflow --separateで復元成功。ただしactiveだったワークフローは自動で無効化される
暗号化キーが異なるまま認証情報をインポートインポートは成功するが、復号を試みると失敗する
N8N_ENCRYPTION_KEYを移行元に揃えて復元をやり直し復号に成功し、平文の認証情報が読める
復元先でワークフローをCLI実行成功。ただし常駐プロセスとポートが衝突しN8N_RUNNERS_BROKER_PORTの指定が必要
復元先でWebhookを再有効化publish:workflowと再起動のセットで、再起動から9.7秒後に200

見落としやすいのは、認証情報のインポート成功が使える状態を意味しない点と、Webhookの稼働状態は復元されない点の2つで、確認方法を本文に示す。

検証環境

  • OS: Windows 11 Home
  • コンテナ: Docker Desktop(Docker Engine 28.3.3)
  • イメージ: n8nio/n8n:2.35.4(docker exec n8n-verify n8n --version2.35.4を確認)
  • シェル: Git Bash(curlはcurl.exe)。PowerShellではcurlInvoke-WebRequestのエイリアスのため、本記事のコマンドはそのままでは動かない
  • 移行元コンテナの起動コマンド: docker run -d --name n8n-verify -p 5678:5678 -v n8n_verify_data:/home/node/.n8n n8nio/n8n:2.35.4
  • 移行元の状態: ワークフロー2件(VerifyGithub0001はname: github-repo-checkでManual Trigger→HTTP Request→Edit Fieldsの3ノード、VerifyWebhook001はname: webhook-echoでactive、Webhook→Edit Fieldsの2ノード)と、検証用に登録した認証情報1件(httpHeaderAuth)を保持
  • 検証日: 2026-08-20 JST。本文のログのタイムスタンプはUTC、Webhook応答のreceivedAtはn8nのデフォルトタイムゾーン(America/New_York、-04:00)

export:workflowとexport:credentialsでバックアップを取る

1. 認証情報を移行元に登録する

検証用のダミー認証情報を平文JSONでインポートした。以下をcreds-in.jsonとして保存する。

[{"id":"VerifyCred0001","name":"verify-header-auth","type":"httpHeaderAuth","data":{"name":"X-Verify","value":"secret123"}}]
docker cp creds-in.json n8n-verify:/tmp/creds-in.json
docker exec n8n-verify n8n import:credentials --input=/tmp/creds-in.json

出力:

Successfully imported 1 credential.

2. export:workflowでワークフローをバックアップする

所要時間も測るため、コンテナ内でtimeを付けて実行した。

docker exec n8n-verify sh -c 'mkdir -p /tmp/backup && time n8n export:workflow --backup --output=/tmp/backup/'

出力:

Successfully exported 2 workflows.
real	0m 4.51s
user	0m 3.85s
sys	0m 1.16s

/tmp/backup/配下にワークフローIDをファイル名としたJSONが2件生成された。

docker exec n8n-verify ls -l /tmp/backup/

出力:

total 8
-rw-r--r--    1 node     node          2745 Aug 19 17:40 VerifyGithub0001.json
-rw-r--r--    1 node     node          2546 Aug 19 17:40 VerifyWebhook001.json

比較のため、全件を1ファイルにまとめる形式と1件のみの指定でも同様に計測した。

docker exec n8n-verify sh -c 'time n8n export:workflow --all --pretty --output=/tmp/backup-all.json'

出力:

Successfully exported 2 workflows.
real	0m 3.59s
user	0m 3.29s
sys	0m 0.87s
docker exec n8n-verify sh -c 'time n8n export:workflow --id=VerifyGithub0001 --output=/tmp/one.json'

出力:

Successfully exported 1 workflow.
real	0m 4.09s
user	0m 3.61s
sys	0m 1.16s

全件1ファイルのサイズも確認した。

docker exec n8n-verify ls -l /tmp/backup-all.json

出力:

-rw-r--r--    1 node     node          5757 Aug 19 17:41 /tmp/backup-all.json

参考にtime n8n --versionreal 0m 0.04sで返るが、これはDB初期化を伴わない別系統の処理であり、エクスポートの所要時間の比較対象にはならない。

VerifyWebhook001.jsonの冒頭を原文のまま示す。

{
  "updatedAt": "2026-08-19T17:39:06.000Z",
  "createdAt": "2026-08-19T17:38:48.277Z",
  "id": "VerifyWebhook001",
  "name": "webhook-echo",
  "description": null,
  "active": true,
  "isArchived": false,
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "order-received",
        "responseMode": "lastNode",
        "options": {}
      },
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        0
      ],
      "webhookId": "b2f9d1c4-verify-0001",
      "id": "a9386f8a-76c0-4574-aa00-39fa82b2c753"
    },

抜粋はここまでで、この後に2ノード目の定義が続く。updatedAtcreatedAtidnameactiveと、各ノードの設定・座標・webhookIdが保存されており、移行元でactiveにしてあったため"active": trueが入っている。この点は後の手順5で効いてくる。

3. export:credentialsで認証情報をエクスポートする

認証情報はワークフローのエクスポートには含まれないため、別コマンドで出力する。

docker exec n8n-verify n8n export:credentials --all --pretty --output=/tmp/creds-enc.json

出力:

Successfully exported 1 credentials.

docker exec n8n-verify cat /tmp/creds-enc.jsonでファイルの中身を確認すると、dataフィールドは暗号文字列になっている(該当行の抜粋):

"data": "U2FsdGVkX1+OoooWC7kbezabhk6lFl09QRFowbW0zTL6uJh3ZnXMmPazI9LEK4UqmZhYfxRAqw4oKvab91ZDBw==",

--decryptedを付けてエクスポートすると平文が出る。

docker exec n8n-verify n8n export:credentials --all --pretty --decrypted --output=/tmp/creds-dec.json
docker exec n8n-verify cat /tmp/creds-dec.json

ファイル内の該当箇所:

    "data": {
      "name": "X-Verify",
      "value": "secret123"
    },

--decryptedの出力は平文のため、Gitリポジトリや共有ストレージのバックアップ先に置いてはいけない。移行に使うのは暗号化エクスポート(--decryptedなし)のファイルで、復元先で復号できるかどうかは暗号化キーが一致しているかで決まる。

暗号化キーは移行元コンテナの/home/node/.n8n/configに初回起動時に自動生成される。このキーがあれば認証情報を復号できるため、キー自体も秘密情報として扱い、共有やコミットをしてはいけない。

docker exec n8n-verify cat /home/node/.n8n/config

出力:

{
	"encryptionKey": "bbV+yjhdxLsO5RFoN/uieXkPrze/Zg/H"
}

import:workflowで別環境にワークフローを復元する

4. バックアップをホストに取り出し、復元先コンテナを起動する

docker cp n8n-verify:/tmp/backup ./backup
docker cp n8n-verify:/tmp/creds-enc.json ./creds-enc.json
docker run -d --name n8n-restore -p 5680:5678 -v n8n_restore_data:/home/node/.n8n n8nio/n8n:2.35.4

-v n8n_restore_data:/home/node/.n8nでボリュームをマウントしている。マウントせずに起動すると、コンテナを削除した時点で復元したワークフローと認証情報も一緒に消える。なお、ここではN8N_ENCRYPTION_KEYをあえて指定せずに起動している。キー不一致時に何が起きるかを手順6で示すためで、実運用の移行では手順7の形で最初からキーを指定して起動する。

5. import:workflowでワークフローを復元する

docker cp ./backup n8n-restore:/tmp/backup
docker exec n8n-restore n8n import:workflow --separate --input=/tmp/backup/

出力:

Deactivating workflow "webhook-echo".
Successfully imported 2 workflows.

--backupはワークフローIDごとの個別ファイルを出力する形式のため、取り込み側もディレクトリを読む--separateを対にして使う。移行元でactiveだったVerifyWebhook001(name: webhook-echo)は、インポート時に自動で無効化される。実際に復元直後のWebhookへPOSTすると404が返る。

curl -s -w "\nHTTP %{http_code}\n" -X POST http://localhost:5680/webhook/order-received -H "Content-Type: application/json" -d '{"item":"widget","qty":3}'

出力の全文:

{"code":404,"message":"The requested webhook \"POST order-received\" is not registered.","hint":"The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"}
HTTP 404

ワークフローの中身は復元されても、稼働状態は別途戻す必要がある。

N8N_ENCRYPTION_KEYが一致しないと認証情報は復号できない

6. キー不一致のままインポートするとどうなるか

復元先を作り直す前に、暗号化エクスポートしたファイルをそのままインポートするとどうなるかを確認した。

docker cp ./creds-enc.json n8n-restore:/tmp/creds-enc.json
docker exec n8n-restore n8n import:credentials --input=/tmp/creds-enc.json

出力:

Successfully imported 1 credential.

暗号化キーが移行元と異なっていても、インポート自体は成功する。復号はインポート時には行われないためだ。しかし、この状態で復号を試みると失敗する。

docker exec n8n-restore n8n export:credentials --all --decrypted --output=/tmp/dec-try.json

出力(同一のエラーメッセージが2回出力される):

Error exporting credentials. See log messages for details.
Credentials could not be decrypted. The likely reason is that a different "encryptionKey" was used to encrypt the data.
Credentials could not be decrypted. The likely reason is that a different "encryptionKey" was used to encrypt the data.

復元先コンテナが自動生成した暗号化キーを確認すると、移行元と一致していない。

docker exec n8n-restore cat /home/node/.n8n/config

出力:

{
	"encryptionKey": "BvsnSZ5HYQm1swpBW0GeyucBUs6SHW5b"
}

インポートの成否だけでは認証情報が使える状態かどうか判断できない。

7. N8N_ENCRYPTION_KEYを揃えて認証情報を正しく復元する

復元先コンテナを、移行元の暗号化キーをN8N_ENCRYPTION_KEYで指定して作り直した。コンテナを削除すると/tmpのファイルも消えるため、docker cpからやり直す。

docker rm -f n8n-restore
docker volume rm n8n_restore_data
docker run -d --name n8n-restore -p 5680:5678 -v n8n_restore_data:/home/node/.n8n -e N8N_ENCRYPTION_KEY="bbV+yjhdxLsO5RFoN/uieXkPrze/Zg/H" n8nio/n8n:2.35.4
docker cp ./backup n8n-restore:/tmp/backup
docker cp ./creds-enc.json n8n-restore:/tmp/creds-enc.json
docker exec n8n-restore n8n import:workflow --separate --input=/tmp/backup/
docker exec n8n-restore n8n import:credentials --input=/tmp/creds-enc.json

ワークフローのインポート出力:

Deactivating workflow "webhook-echo".
Successfully imported 2 workflows.

認証情報のインポート出力:

Successfully imported 1 credential.

いずれも作り直し前と同一の出力である。この状態で復号を試みる。

docker exec n8n-restore n8n export:credentials --all --decrypted --pretty --output=/tmp/dec-ok.json
docker exec n8n-restore cat /tmp/dec-ok.json

エクスポートの出力:

Successfully exported 1 credentials.

ファイル内の該当箇所は平文になっている。

    "data": {
      "name": "X-Verify",
      "value": "secret123"
    },

復号が成功し、暗号化キーが一致していることの確認になる。認証情報を含めて別環境に移行する場合、N8N_ENCRYPTION_KEYを移行元の/home/node/.n8n/configの値に合わせて復元先を作ることが必須の手順になる。

復元した環境でワークフローとWebhookを動かす

8. 復元先でワークフローを実行する

環境変数なしでn8n executeを実行すると、コンテナ内の常駐n8nプロセスがタスクブローカーの5679番ポートを使用中のため失敗する。

docker exec n8n-restore n8n execute --id=VerifyGithub0001

出力:

n8n Task Broker's port 5679 is already in use. Do you have another instance of n8n running already?

N8N_RUNNERS_ENABLED=falseを単独で指定しても同じエラーで失敗する。

docker exec -e N8N_RUNNERS_ENABLED=false n8n-restore n8n execute --id=VerifyGithub0001

出力:

n8n Task Broker's port 5679 is already in use. Do you have another instance of n8n running already?

復元先コンテナでも原因を切り分けた結果、N8N_RUNNERS_BROKER_PORTでブローカーのポートを変更した場合のみ成功した。実際の出力は実行データを含む長いJSONのため、成否を示す行をgrepで絞って確認した。実行したコマンド(--idは自環境のワークフローIDに読み替える):

docker exec -e N8N_RUNNERS_BROKER_PORT=5690 n8n-restore n8n execute --id=VerifyGithub0001 2>&1 | grep -E 'Execution was|"status"|"finished"'

出力:

Execution was successful:
  "status": "success",
  "finished": true

復元したワークフローがそのまま動作する。

9. Webhookを再有効化する

docker exec n8n-restore n8n publish:workflow --id=VerifyWebhook001

出力:

Publishing workflow with ID: VerifyWebhook001 (current version)
Note: Changes will not take effect if n8n is running.
Please restart n8n for changes to take effect if n8n is currently running.

出力の注記どおり、DBへの反映だけではWebhookのエンドポイントが登録されないため再起動する。

再起動後は、200かつJSONボディが返るまで1秒間隔でポーリングし、経過時間とボディを表示するループを実行した。完了条件を「200かつボディの先頭が{」としたのは、起動途中のn8nがWebhook URLに対してHTTP 200でボディn8n is starting up. Please waitという文字列を返す瞬間があるためである(同一構成の別コンテナの再起動時に観測)。ステータスコードのみを条件にすると、起動中に返るこの仮の応答を成功と誤判定する。

  • 本番URLへのPOSTは、200が返った時点で復元したワークフローを実際に実行する
  • 実運用のワークフローを復元した場合は副作用のないペイロードを使うか、疎通確認自体をdocker logsEditor is now accessible行の監視で代替する

実際に実行したコマンドは次のとおり(掲載のまま実行した)。

docker restart n8n-restore > /dev/null
s=$(date +%s.%N)
until c=$(curl -s --max-time 5 -o wh.json -w '%{http_code}' -X POST http://localhost:5680/webhook/order-received -H 'Content-Type: application/json' -d '{"item":"widget","qty":3}'); [ "$c" = "200" ] && [ "$(head -c 1 wh.json 2>/dev/null)" = "{" ]; do sleep 1; done
awk -v t="$(date +%s.%N)" -v r="$s" 'BEGIN{printf "HTTP 200 elapsed +%.1fs from restart\n", t-r}'
cat wh.json

出力:

HTTP 200 elapsed +9.7s from restart
{"item":"widget","qty":3,"receivedAt":"2026-08-19T14:22:48.965-04:00"}

バックアップと復元の実測結果

  • ワークフローエクスポートの所要時間(コンテナ内のtimeコマンドで各1回計測): --backup(2件)4.51秒、--all1ファイル(2件)3.59秒、--id1件のみ4.09秒。1件の4.09秒に対し2件が4.51秒と3.59秒で、実行ごとのばらつきが件数差より大きく、この規模では件数の影響は読み取れなかった。所要時間の大半はn8n CLIプロセスの起動時間とみられる。n8n --versionは0.04秒で返るが、DB初期化を伴わない別系統の処理のため、この比較の基準には使えない
  • バックアップファイルサイズ: VerifyGithub0001.json 2,745バイト、VerifyWebhook001.json 2,546バイト、全件1ファイル5,757バイト
  • ワークフロー復元: Deactivating workflow "webhook-echo". Successfully imported 2 workflows.。activeだったワークフローは自動で無効化され、復元直後のPOSTは404(not registered)を返す
  • 認証情報の暗号化キー不一致: インポートはSuccessfully imported 1 credential.で成功するが、--decryptedエクスポートはCredentials could not be decrypted. The likely reason is that a different "encryptionKey" was used to encrypt the data.で失敗する
  • 認証情報の正しい移行: 復元先を移行元と同じN8N_ENCRYPTION_KEYで作り直すと--decryptedエクスポートが成功し、平文{"name": "X-Verify", "value": "secret123"}が読める
  • 復元先コンテナにボリューム(-v n8n_restore_data:/home/node/.n8n)をマウントしていないと、コンテナ削除で復元結果ごと消える
  • 復元先でのワークフロー実行に必要な環境変数はN8N_RUNNERS_BROKER_PORTのみで、N8N_RUNNERS_ENABLED=falseは不要であることを復元先コンテナ自体で確認した
  • Webhookの再有効化はpublish:workflowdocker restartのセットで、再起動から9.7秒後に200を確認(1秒間隔ポーリング、200かつJSONボディを完了条件とするループで計測)

復元結果をn8nの画面で確認する

復元の結果はブラウザからも確認できる。以下のスクリーンショットは、本記事と同一の手順を同じ検証環境で再実行して取得したもので、本文に引用したログとは別の試行である。再実行の復元先は手順7と同じくN8N_ENCRYPTION_KEYを移行元に揃えて起動した構成で、--decryptedエクスポートの成功まで確認している。いずれも手順9の再有効化を行う前の状態である。各画像はアドレスバーを含めて撮影しており、localhost:5678が移行元、localhost:5680が復元先である。UIの閲覧は検証の本筋ではないため手順には含めない。初回アクセス時のみオーナーアカウントの作成を求められ、メールアドレス・氏名・パスワードを登録する。以下は両インスタンスとも作成を済ませた状態の画面である。

移行元のワークフロー一覧では、有効化済みのwebhook-echoにPublishedバッジが付いている。PublishedはこのバージョンのUIで有効化状態を示す表示で、本文のpublish:workflowに対応する。

移行元localhost:5678のワークフロー一覧。webhook-echoにPublishedバッジが付いている

復元先の一覧にはワークフロー2件が復元されているが、webhook-echoにPublishedバッジは付いていない。手順5のDeactivating workflow "webhook-echo".の出力どおり、復元時に無効化されたためである。

復元先localhost:5680のワークフロー一覧。2件とも復元され、webhook-echoにPublishedバッジは付いていない

Credentialsタブにはverify-header-authが表示される。ただし手順6で示したとおり、一覧に表示されることはインポートが通ったことを示すだけで、復号できるかどうかはこの画面からは分からない。キーの一致は手順7の--decryptedエクスポートの成功で確認する。

復元先localhost:5680の認証情報一覧。verify-header-authが表示されている

まとめ

  • n8nのCLIによるバックアップと復元は、ワークフローだけならexport:workflow --backupimport:workflow --separateで完結する
  • 認証情報まで含めて別環境に移行する場合は、暗号化キー(N8N_ENCRYPTION_KEY)を移行元と揃えることが復元の成否を分ける
  • インポートが成功しても復号できるとは限らない点は見落としやすい
  • activeなWebhookワークフローは復元時に無効化されるため、再有効化と再起動までを手順に含める必要がある

出典