n8n を Zeabur から VPS へ移行:encryption key を控え忘れると credentials は全滅(検証済み)

n8n の移行でいちばん事故が起きやすいのは N8N_ENCRYPTION_KEY です。検証では、誤ったキーでインポートしても n8n は成功と表示しました。キーの探し方、エクスポートとインポート、そのまま使える Docker Compose と Caddy の設定をまとめました。

要点

  • n8n の credentials は encryption key で暗号化されている。キーが変わると、すべて復号できなくなる。
  • キーは環境変数 N8N_ENCRYPTION_KEY か、n8n フォルダ内の config ファイルにある。
  • 検証結果:誤ったキーでインポートしても成功と表示される。export:credentials --decrypted で必ず確認する。
  • そのまま使える Docker Compose + Caddy の設定(HTTPS 自動化)付き。
この記事の検証方法(実機検証 4 件、公式ドキュメント準拠 3 件)
実際に検証した内容
  • 公式 n8n 2.43.7 イメージ:export:workflow と export:credentials でエクスポートし、新しい n8n へインポート
  • 同じ encryption key では credentials を復号できた。別のキーではインポートは成功と表示されるが、後で復号に失敗
  • 環境変数がない場合、n8n はキーを自動生成して /home/node/.n8n/config に書き込む
  • 本記事の docker-compose.yml と Caddyfile:ローカルで n8n.localhost として起動し、Caddy 経由の HTTPS で n8n に到達、healthz は 200
公式ドキュメントに基づく内容(未検証)
  • Zeabur ダッシュボードでのコマンド実行とファイルのダウンロード:Zeabur 公式ドキュメントに基づいて記載。Zeabur での操作は行っていません
  • Zeabur の n8n テンプレートのデータ保存先、SQLite と PostgreSQL のどちらを使うか:ご自身のサービス設定を確認してください
  • 本番ドメインでの Let's Encrypt 証明書の自動取得:ローカル検証では Caddy の自己署名証明書を使用。本番の挙動は Caddy の公式ドキュメントによります
目次(6 項目)
  1. いちばん大事なこと:encryption key を見つける
  2. ステップ 1:Zeabur でエクスポート
  3. ステップ 2:VPS に新しい n8n を構築
  4. ステップ 3:インポート
  5. ステップ 4:credentials が本当に復号できるか検証
  6. ステップ 5:仕上げ

いちばん大事なこと:encryption key を見つける

n8n が保存している credentials(API キー、OAuth トークン、データベースのパスワード)は、すべて 1 つの encryption key で暗号化されています。別のマシンに移ってキーが変わると、credentials は 1 つも復号できず、すべて設定し直すことになります。

キーは次のどちらかにあります。

  1. 環境変数 N8N_ENCRYPTION_KEY:Zeabur のサービスの「変数」タブで確認します
  2. n8n フォルダ内の config ファイル:環境変数が設定されていない場合、n8n は初回起動時にキーを自動生成してこのファイルに書き込みます。公式イメージでのパスは /home/node/.n8n/config で、中身は次のようになっています。
{
	"encryptionKey": "ランダムな文字列"
}

キーを控えて、パスワードマネージャーに保存してください。

検証で見つかった落とし穴:誤ったキーでインポートしても、n8n はエラーを出さない。

KEY_A でエクスポートした credentials を、KEY_B を設定した n8n にインポートしたところ、Successfully imported 1 credential. と表示され、一見問題ありませんでした。実際に使おうとしたときに初めて次のエラーが出ます。

Credentials could not be decrypted. The likely reason is that a different "encryptionKey" was used to encrypt the data.

移行後は必ず下の「ステップ 4」で検証してください。インポート成功のメッセージを信じてはいけません。

ステップ 1:Zeabur でエクスポート

Zeabur のドキュメントによると、サービスの「Overview」タブにある「Command」セクションで、コンテナ内でコマンドを実行できます。workflow と credentials を n8n フォルダにエクスポートします。

n8n export:workflow --all --output=/home/node/.n8n/workflows.json
n8n export:credentials --all --output=/home/node/.n8n/credentials.json

同じタブの「Files」セクションでコンテナ内のファイルを閲覧・ダウンロードできます。この 2 つのファイルと config をダウンロードしてください。

エクスポートされた credentials.json は暗号化されていて、開いてもパスワードは読めません。検証では、設定した SECRET123 という値はファイル内のどこにも見つかりませんでした。だからこそ、元のキーとセットでなければ役に立ちません。

別の方法:ボリュームを丸ごとバックアップする。 n8n が内蔵の SQLite を使っている場合(フォルダに database.sqlite がある)、Zeabur の「Backup」タブのオフラインバックアップでフォルダ全体をダウンロードできます。Zeabur のドキュメントでは先にサービスを停止する(Settings → Suspend Service)必要があり、これは正しい手順です。実行中の SQLite には -wal ファイルがあり、停止せずにコピーするとデータが不完全になるおそれがあります。

ステップ 2:VPS に新しい n8n を構築

Ubuntu の VPS を用意して Docker をインストールし、ドメイン(例:n8n.example.com)の A レコードを VPS の IP に向けてから、次の 2 つのファイルを作成します。

# docker-compose.yml
services:
  n8n:
    image: docker.n8n.io/n8nio/n8n
    restart: unless-stopped
    environment:
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}   # Zeabur で使っていたキーをそのまま使う
      - N8N_HOST=n8n.example.com
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://n8n.example.com/
    volumes:
      - n8n_data:/home/node/.n8n
  caddy:
    image: caddy:2
    restart: unless-stopped
    ports: ["80:80", "443:443"]
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
volumes:
  n8n_data:
  caddy_data:
# Caddyfile
n8n.example.com {
  reverse_proxy n8n:5678
}
echo "N8N_ENCRYPTION_KEY=Zeabur から控えたキー" > .env
docker compose up -d

DNS がこの VPS を向いていて、80 番と 443 番ポートが外部に開いていれば、Caddy が HTTPS 証明書を自動で取得します。n8n をリバースプロキシの後ろで動かす場合、n8n のドキュメントでは WEBHOOK_URL の設定が求められています。これにより、n8n が生成する Webhook URL が正しい公開 URL になります。

ステップ 3:インポート

docker compose cp workflows.json n8n:/tmp/
docker compose cp credentials.json n8n:/tmp/
docker compose exec n8n n8n import:workflow --input=/tmp/workflows.json
docker compose exec n8n n8n import:credentials --input=/tmp/credentials.json

ステップ 4:credentials が本当に復号できるか検証

docker compose exec n8n n8n export:credentials --all --decrypted --output=/tmp/check.json
docker compose exec n8n cat /tmp/check.json
  • 平文の内容が読める:キーは正しい
  • Credentials could not be decrypted と出る:キーが違うので、最初の節に戻って確認

確認したらすぐにファイルを削除してください。 平文のパスワードが含まれています。

docker compose exec n8n rm /tmp/check.json /tmp/credentials.json

ステップ 5:仕上げ

  • 各 workflow を開いてトリガーを確認する。Webhook で起動するものは、相手側のサービス(LINE、Stripe、フォームツール)で URL を新しいものに変更する
  • Zeabur ではセキュリティインシデントが起きているため、credentials 内のサードパーティ API キーはすべて差し替えることをおすすめします(セキュリティ編を参照)
  • 毎日のバックアップを設定する:最低でも毎日 n8n_data ボリュームをアーカイブし、別の場所に保管する

Dify や LobeChat のように複数のサービスで構成されるツールは、公式リポジトリの docker-compose.yml を使い、同じ方法で Caddy を追加すれば構築できます。データベースの移行はデータベース編を参照してください。

よくある質問

n8n の encryption key はどこにありますか?

環境変数 N8N_ENCRYPTION_KEY が設定されていればそこにあります。設定されていない場合、n8n が初回起動時にキーを自動生成し、n8n フォルダの config ファイル(公式イメージでは /home/node/.n8n/config)に書き込みます。

credentials のインポートは成功と出たのに、使えないのはなぜですか?

encryption key が違うためです。n8n はインポート時にキーが正しいかを確認せず、使うときになって初めて Credentials could not be decrypted が表示されます。元のキーで再インポートしてください。