n8nを止めずに運用する:アップデート・バックアップ・エラー通知・脆弱性

公開 書いた人 大林 諒

ワークフローは作った日より、動き続けている日のほうがずっと長くなります。セルフホストのn8nで困るのは、作るときではなく、止まったことに誰も気づかない日です。ここでは「問い合わせ受付」ワークフローを例に、エラーの通知、外からの見張り、実行履歴の掃除、バックアップ、アップデート、脆弱性への対応までを一通り組みます。前提は、VPSに公式のDocker Compose構成でn8nを立て、n8n.example.co.jp のような独自ドメインとHTTPSで使っている状態です。版はn8n 2.44.7(2026年10月時点のstable)で確かめています。

この記事の内容
  1. 先に決めること
  2. 運用でやることの一覧
  3. 手順
  4. 応用
  5. できないことと、その代わり
  6. よく詰まるところ
  7. 次にやること

先に決めること

エラーの通知を業務の通知と分ける

「問い合わせ受付」は #問い合わせ に新着を流します。エラーの通知も同じチャンネルに流すと、新着に埋もれて誰も読まなくなります。実際に本番で動かしていると、エラーを業務の通知と同じ場所に流していたせいで、数日分の失敗に誰も気づかなかったことがありました。エラー専用に #n8n-監視 を作り、見る担当と、その人が休むときの代わりを決めます。

見張りはn8nの外に置く

n8nの中に作ったエラー通知は、n8nが動いているときしか働きません。n8nそのものが落ちた、証明書が切れた、VPSが止まった、という止まり方は外から見張るしかありません。外部の死活監視サービスを1つ使うと決めます。

版は番号で固定する

イメージのタグを stable のままにすると、再起動のついでに知らない版へ上がることがあります。番号で固定し、月に1回、自分のタイミングで上げます。

バックアップと暗号鍵の置き場所

バックアップをVPSの中にだけ置くと、VPSごと失ったときに一緒に消えます。送り先はVPSの外(別のサーバー、オブジェクトストレージ、社内のNASなど)にします。Credential(認証情報)を暗号化している鍵 N8N_ENCRYPTION_KEY は、社内のパスワード管理ツールに別にしまいます。

運用でやることの一覧

やること頻度使うもの
エラー用ワークフロー常時Error Trigger、Slack
外からの死活監視常時/healthz/readiness、外部の死活監視サービス
毎朝の生存確認毎日Schedule Trigger
実行履歴の削除常時(自動)EXECUTIONS_DATA_PRUNE ほか
バックアップ毎日CLIのエクスポートと、データフォルダの丸ごと保存
戻せるかの試し毎月手元のPCのDocker
アップデート毎月release notes、docker compose pull
脆弱性の確認毎週GitHub Security Advisories
セキュリティ監査毎月n8n audit

手順

1. エラー用ワークフローを作る

ワークフローが失敗したら #n8n-監視 に知らせるワークフローを1本作ります。1本を複数のワークフローで共有できます。

  1. 新しいワークフローを作り、名前を「エラー通知」にします。
  2. 最初のノードに「Error Trigger」(ほかのワークフローが失敗したときに動くトリガー)を置きます。
  3. 「Slack」ノードを足し、Resourceを「Message」、Operationを「Send」にします。
  4. 「Send Message To」を「Channel」にし、「Channel」に #n8n-監視 を指定します。「Message Text」に下の式を入れて保存します。
n8nでエラーが起きました
ワークフロー: {{ $json.workflow.name }}
止まったノード: {{ $json.execution?.lastNodeExecuted || 'トリガー' }}
内容: {{ $json.execution?.error?.message ?? $json.trigger.error.message }}
実行の画面: {{ $json.execution?.url || '(保存されていません)' }}
Error Trigger と Slack ノードの2つだけを置いたエラー用ワークフロー
Error Trigger と Slack ノードの2つだけを置いたエラー用ワークフロー

トリガーそのもので失敗したときは execution 自体が無く、代わりに trigger.error に内容が入ります。式で両方を見て ?. を付けているのはこのためで、$json.execution.lastNodeExecuted のように書くと、トリガーで失敗したときだけその欄が空の通知が届きます。Error Triggerを使うワークフローは公開(Publish)しなくても動きます。

次に、見張る側のワークフローで指定します。

  1. 「問い合わせ受付」を開き、ワークフロー名の右にある「…」メニューから「Settings」を開きます。
  2. 「Error Workflow」で「エラー通知」を選び、保存します。
ワークフローの Settings で Error Workflow に「エラー通知」を選んだところ
ワークフローの Settings で Error Workflow に「エラー通知」を選んだところ

最後に試します。Error Triggerは手動実行の失敗では動きません。「Execute workflow」(ワークフローを実行)を押して試しても通知は来ないので、試験用のワークフローを作ります。

  1. 「Schedule Trigger」(決まった時刻に動くトリガー)を置き、Trigger Intervalを「Minutes」、間隔を1分にします。
  2. 「Stop and Error」(わざとエラーで止めるノード)をつなぎます。
  3. Settingsの「Error Workflow」で「エラー通知」を選び、保存して公開します。
  4. #n8n-監視 に通知が来たら公開を取り消します。

2. 失敗の扱いをノードごとに決める

エラー用ワークフローが拾うのは、ワークフローが失敗で終わったときだけです。ノードの「Settings」タブの「On Error」を変えると、通知の届き方も変わります。

設定動き通知
Stop Workflowその場で止める(既定)届く
Continue直前の正しいデータで次へ進む届かない
Continue (using error output)エラーの内容を別の出口に流す出口の先で止めれば届く

「Continue」は失敗が黙って消えます。運用しているn8nで、顧客を探すノードを「Continue」にしていたため、検索そのものが失敗したときも「該当なし」として流れ、実在する顧客からの連絡を取りこぼしたことがありました。続行させるなら「Continue (using error output)」にし、エラーの出口の先に「Stop and Error」を置きます。

ノードの Settings タブで On Error の選択肢を開いたところ
ノードの Settings タブで On Error の選択肢を開いたところ

3. n8nの外から見張る

止まり方実行一覧に残るか見張り方
コンテナやVPSが止まった残らない外部から /healthz/readiness を見る
証明書の期限が切れた残らない外部から証明書の残り日数を見る
定期実行が黙って止まった残らない毎朝の生存確認
メール受信のトリガーが再接続に失敗した残らない毎朝の生存確認とトリガーの設定

n8nには死活確認の入口が2つあります。/healthz はn8nに届けば200を返しますが、データベースの状態は見ていません。/healthz/readiness はデータベースにつながり、移行も済んでいるときだけ200を返します。外部の死活監視サービスには https://n8n.example.co.jp/healthz/readiness を登録し、数分おきに見てもらいます。証明書の残り日数も見られるなら、30日を切ったら知らせる設定にします。

証明書は特に危ないところです。運用しているn8nで証明書の自動更新が止まり、期限が切れたことがありました。フォームからWebhookへの送信がHTTPSのエラーで弾かれるので、n8nには何も届きません。実行もエラーも残らず、通知が全部止まったことに気づいたのは問い合わせの件数が減ってからでした。

4. 毎朝の生存確認を作る

  1. 新しいワークフロー「毎朝の生存確認」に「Schedule Trigger」を置き、Trigger Intervalを「Days」、Trigger at Hourを8にします。
  2. 「HTTP Request」ノードで、外部の死活監視サービスが発行するハートビート用のURLへGETを送ります。
  3. 「Slack」ノードで #n8n-監視 に「n8nは動いています」と送ります。
  4. 保存して公開します。

人は来なかった通知に気づけません。ハートビート監視は「決まった時間までに連絡が無ければ知らせる」仕組みなので、こちらを本命にします。

メール受信のトリガーには別の注意があります。運用しているn8nで、「Email Trigger (IMAP)」(メール受信で動くトリガー)がメールサーバー側の長時間の切断のあと再接続に失敗し、ワークフローごと止まったことがありました。起動時の失敗なので実行一覧には何も残りません。このトリガーには「Force Reconnect Every Minutes」(決まった分数ごとに接続し直す)があるので、数十分おきの値を入れます。短くしすぎると接続の回数が増え、失敗の機会も増えます。Gmailなら、Gmailのノードで受け取る形に替えると問題そのものが消えます。

2.45.0からは定期実行の仕組みが「durable scheduler」に変わり、既定でオンになります。n8nが止まっていた間に予定時刻を過ぎた回は、1分(既定の猶予)以内なら遅れて実行され、それを過ぎた回は既定では捨てられて、次の予定から再開します。生存確認が来なかった朝は、ほかの定期実行も止まっていたと考えて実行一覧を確かめます。

5. 実行履歴の保存と削除を決める

n8nは既定でSQLiteを使い、ワークフロー、Credential、実行履歴を ~/.n8n/database.sqlite(Dockerならコンテナの /home/node/.n8n)に入れています。

環境変数既定値意味
EXECUTIONS_DATA_PRUNEtrue古い実行履歴を自動で消す
EXECUTIONS_DATA_MAX_AGE336この時間より古い履歴を消す(336時間=14日)
EXECUTIONS_DATA_PRUNE_MAX_COUNT10000残す件数の上限。0 で無制限
EXECUTIONS_DATA_SAVE_ON_ERRORall失敗した実行を保存する
EXECUTIONS_DATA_SAVE_ON_SUCCESSall成功した実行を保存する
DB_SQLITE_VACUUM_ON_STARTUPfalse起動時にデータベースを詰め直す

「問い合わせ受付」程度なら既定のままで困りません。1分おきに動くワークフローが増えてきたら、成功した実行を残さない設定にします。失敗した実行は残します。残さないと、エラー通知に実行の画面のリンクが出ません。.env に次を足します。

EXECUTIONS_DATA_MAX_AGE=168
EXECUTIONS_DATA_SAVE_ON_SUCCESS=none
EXECUTIONS_DATA_SAVE_ON_ERROR=all

ワークフローごとに決めたいときは、Settingsの「Save successful production executions」で変えられます。

SQLiteは、履歴を消してもファイルが小さくなりません。空いた場所が使い回されるだけです。小さくしたいときは DB_SQLITE_VACUUM_ON_STARTUP=true で一度だけ再起動し、終わったら false に戻します。起動に時間がかかり、その間は何も動かないので夜間に行います。運用しているn8nでデータベースが1GBを超えたとき、コピーだけで10分以上かかるようになりました。

ワークフローの Settings で Save successful production executions を開いたところ
ワークフローの Settings で Save successful production executions を開いたところ

6. バックアップを取る

方法入るもの入らないもの
CLIのエクスポートワークフロー、Credentialユーザーと権限、実行履歴、Variables、暗号鍵を含む設定
データフォルダの丸ごと保存上のすべて(SQLiteの場合)外に置いたPostgreSQLなど

丸ごと戻せるのはデータフォルダのほうです。両方を毎日取ります。

丸ごと保存はn8nを止めてから取ります。動いている最中にデータベースをコピーすると、中身の食い違ったバックアップになることがあります。2.xのSQLiteは既定でWALモードで動き(2.44.7で確かめています)、database.sqlite の横に -wal と -shm のファイルができます。直近の書き込みはWALの側にあることがあり、運用しているn8nで database.sqlite だけをコピーしたら直前の変更が入っていなかったことがありました。フォルダごと保存すれば全部入ります。

下は毎日3時に動かすスクリプトです。止まるのは数十秒から数分なので、問い合わせの少ない時間帯を選びます。ボリューム名は docker volume ls で確かめます(公式構成なら n8n_n8n-data)。ワークフローとCredentialは別のフォルダに書き出します。同じフォルダに混ぜると、Credentialの読み込みが失敗します。

#!/bin/sh
set -eu
cd ~/n8n
DAY=$(date +%F)
mkdir -p backups
docker compose exec -T -u node n8n n8n export:workflow --backup --output=/home/node/.n8n/backups/workflows/
docker compose exec -T -u node n8n n8n export:credentials --backup --output=/home/node/.n8n/backups/credentials/
docker compose stop n8n
docker run --rm -v n8n_n8n-data:/data:ro -v "$PWD/backups":/backup alpine \
  tar czf "/backup/n8n-data-$DAY.tgz" -C /data .
docker compose start n8n
scp "backups/n8n-data-$DAY.tgz" backup@backup.example.co.jp:/srv/n8n-backups/
find backups -name 'n8n-data-*.tgz' -mtime +14 -delete

~/n8n/backup.sh に保存して実行権を付け、crontab -e に 0 3 * * * /home/ubuntu/n8n/backup.sh >> /home/ubuntu/n8n/backup.log 2>&1 を足します。--backup は全件を1本ずつ別のJSONに書き出す指定です。Credentialは暗号化されたまま出ます。--decrypted を付けると平文で出るので、普段は使いません。

暗号鍵は別に保管します。n8nは初回起動時に鍵を作り、~/.n8n の設定ファイル(config)に保存します。鍵が違うn8nにデータを戻すと、Credentialは1つも開けません。.env に N8N_ENCRYPTION_KEY をまだ書いていないなら、docker compose exec -u node n8n cat /home/node/.n8n/config で今の鍵を写して .env に書き、同じ値をパスワード管理ツールにしまいます。新しい鍵を作るのではなく、今の鍵を写すところが大事です。

7. 戻せるかを試す

バックアップは、戻せると確かめるまでは無いのと同じです。月に1回、手元のPCで試します。

  1. 手元のPCに本番と同じ構成を用意し、.env の N8N_VERSION と N8N_ENCRYPTION_KEY を本番と同じにします。
  2. 起動する前に、空のボリュームへバックアップを展開します。
  3. 起動したらすぐ、公開中のワークフローを全部取り消します。残すと定期実行が手元でも動き、Slackへ二重に投稿されます。
  4. 本番と同じアカウントでログインできるか確かめます。
  5. 「問い合わせ受付」を開き、GoogleスプレッドシートのCredentialで接続の確認が通るか確かめます。
  6. 終わったら手元の環境を消します。
docker volume create n8n_n8n-data
docker run --rm -v n8n_n8n-data:/data -v "$PWD":/backup alpine \
  tar xzf /backup/n8n-data-2026-10-26.tgz -C /data
docker compose up -d
docker compose exec -u node n8n n8n unpublish:workflow --all
手元に戻したn8nでCredentialを開いたところ(値は伏せ字)
手元に戻したn8nでCredentialを開いたところ(値は伏せ字)

8. アップデートする

公式は少なくとも月に1回の更新を勧めています。何版もまとめて飛ばすと、壊れたときに原因を追えなくなります。タグは stable が本番向け、beta がいちばん新しく不安定なこともある版です。2.0からタグ名が latest は stable に、next は beta に変わり、公式も番号での固定を勧めています。公式のDocker Compose構成では、.env の N8N_VERSION がn8n本体とrunnersの版を決めます。サンドボックス用の3つのイメージは別の N8N_SANDBOX_VERSION で固定されているので、本体とは別に扱います。

  1. release notesで、今の版から上げる版までの breaking changes(互換性のない変更)を読みます。
  2. 手順6のスクリプトを手で動かし、更新前のバックアップを取ります。
  3. .env の N8N_VERSION を上げる版の番号に書き換えます。
  4. docker compose pull で新しいイメージを取ってきます。
  5. docker compose down で今のコンテナを止めます。
  6. docker compose up -d で起動します。
  7. docker compose logs -f n8n で、データベースの移行が終わりエラーが無いことを見ます。
  8. /healthz/readiness が200を返すか確かめます。
  9. フォームから試しに1件送り、シートとSlackまで届くか確かめます。
release notes のページ。左のメニューに v3.0・v2.0 の Breaking changes が並び、各版の項目が新しい順に続く
release notes のページ。左のメニューに v3.0・v2.0 の Breaking changes が並び、各版の項目が新しい順に続く

公式の更新手順には戻し方が無く、バックアップと復元のページに、データフォルダを丸ごと戻す復元の手順があります。新しい版は起動時にデータベースを移行することがあるので、番号だけを戻すと古い版が新しい形のデータベースを読むことになります。戻すときは、番号を戻したうえで手順2のデータフォルダを丸ごと戻します。更新後に受けた問い合わせの実行履歴は消えるので、戻す前にシートで確かめておきます。

1.xから上げる場合は、2.0の変更点を先に読みます。運用に効くのは次のあたりです。

2.0での変更対処
「Activate」が「Publish」になり、編集しただけでは本番に出ない編集後に公開し直す
Codeノードから環境変数が読めない値をCredentialや「Edit Fields」に移す
「Execute Command」が既定で無効使うなら NODES_EXCLUDE を見直す
.env の読み方が変わったバッククォートを含む値は引用符で囲む。行頭が # の行はコメント扱い

公式は検証用のn8nで先に試すことも勧めていますが、そのためのEnvironments機能はBusinessとEnterpriseのプランのものです。無料のセルフホストなら、手順7で戻した手元のn8nに先に新しい版を当てて動きを見ます。

9. 脆弱性に備える

n8nの脆弱性は、GitHubの n8n-io/n8n の Security Advisories に載ります。一覧に件名、重大度(Critical、High、Moderate、Low)、公開日が並び、1件を開くと「Affected versions」(影響を受ける版)、「Patched versions」(直った版)、回避策が書かれています。

たとえば2026年10月14日には10件がまとめて公開されました。そのうち「Webhook Responses Without the Sandbox Policy Allow Stored Cross-Site Scripting」は重大度High(CVSS 8.4)で、2.44.1と2.43.5で直っています。2.44.7なら当たりません。こうして今の版が直った版より新しいかを1件ずつ見ます。

Security Advisories の1件を開いたときの Affected versions・Patched versions の欄
Security Advisories の1件を開いたときの Affected versions・Patched versions の欄

HighかCriticalが今の版に当たっていたら、その日のうちに直った版へ上げます。すぐ上げられないときは、advisoryの回避策を当てます。回避策は応急処置で、更新の代わりにはなりません。上げたあとは、見覚えのない実行やユーザー、Credentialが増えていないかを確かめます。GitHubでリポジトリの「Watch」を「Custom」にしてReleasesを選ぶと、新しい版が出たときに通知が来ます。

10. 公開する範囲を絞る

外から触れる場所が少ないほど、脆弱性の被害は小さくなります。advisoryの回避策にも、使う人を信頼できる人に限ること、/webhook/ への接続を信頼できる送り元に限ることが出てきます。

「問い合わせ受付」で外に開ける必要があるのは、フォームから呼ばれる /webhook/ と、外部監視が見る /healthz だけです。編集画面は会社のIPからだけ開けるようにします。n8nの前にnginxを置いた場合の例です。

server {
    listen 443 ssl;
    server_name n8n.example.co.jp;

    location /webhook/ {
        proxy_pass http://127.0.0.1:5678;
    }
    location /healthz {
        proxy_pass http://127.0.0.1:5678;
    }
    location / {
        allow 203.0.113.10;
        deny all;
        proxy_pass http://127.0.0.1:5678;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

証明書の設定は導入時のものを使います。あわせてDocker Composeの ports を '127.0.0.1:5678:5678' に変えます。公式の構成は5678番を外に開けているので、そのままだとnginxを通らず直接入れてしまいます。Waitノードで外からの再開を待つワークフローがあるなら /webhook-waiting/ も開けます。在宅の人が多くIPで絞れない会社は、編集画面の側にnginxのBasic認証をかけるか、VPNを通します。

対策やり方
使わないなら公開APIを止めるN8N_PUBLIC_API_DISABLED=true、N8N_PUBLIC_API_SWAGGERUI_DISABLED=true
2段階認証各自が「Settings」→「Personal」→「Enable 2FA」。回復用コードを保管する
2段階認証の強制「Settings」→「Security & policies」の「Enforce two-factor authentication」(セルフホストはBusiness以上)
アカウントを共有しない使う人ごとにユーザーを作り、辞めた人はその日に消す
2.0で厳しくなった既定値を戻さないCodeノードの環境変数の遮断、Execute Commandの無効を残す
Settings の Personal で Enable 2FA を押したあとのQRコードの画面
Settings の Personal で Enable 2FA を押したあとのQRコードの画面

最後にセキュリティ監査を流します。docker compose exec -u node n8n n8n audit を実行すると、使われていないCredential、SQLを式で組み立てているノード、ホストのファイルやコードに触れるノード、守られていないWebhook、古い版のままかどうかを返します。

11. 毎週・毎月の点検表を回す

頻度見るものおかしいときにやること
毎日朝の「動いています」が来ているか外部監視とVPSを確かめる
毎週「Executions」で Status を「Error」に絞った一覧通知が来ていない失敗があれば Error Workflow の指定漏れを直す
毎週Security Advisories手順9のとおり動く
毎週VPSの外に昨日のバックアップが届いているかbackup.log を読む
毎月release notes と1つ上の版手順8で上げる
毎月手元に戻してCredentialが開くか手順7
毎月docker system df -v でのボリュームの容量保存する履歴を減らす
毎月n8n audit の結果とユーザー一覧使っていないCredentialとユーザーを消す
実行一覧で Workflows を絞り、Status を Error に絞り込んだところ
実行一覧で Workflows を絞り、Status を Error に絞り込んだところ

応用

誤報を減らす

一時的な失敗まで全部知らせると、#n8n-監視 がまた読まれなくなります。まず失敗しやすいノードの「Settings」で「Retry On Fail」(失敗したらやり直す)を付けます。それでも多いときは、エラー用ワークフローの中で「Wait」ノードを30分置き、同じワークフローがまだ失敗しているかを確かめてから知らせます。実際に本番で動かしていると、この形にしてから夜中の誤報がほとんど無くなりました。

業務の異常もエラーにする

n8nが「成功」と言っても、業務として正しいとは限りません。フォームのデータにメールアドレスが無い、シートに書けた件数が0件、といった場合は「IF」ノードで判定して「Stop and Error」で止めます。業務の異常もエラー用ワークフローへ同じ形で届きます。

監査を毎月自動で流す

セキュリティ監査は「n8n」ノードからも動かせます。Resourceを「Audit」、Operationを「Generate」にし、毎月1日の「Schedule Trigger」につないで結果をSlackへ送ります。このノードはn8nのAPIキーで公開APIを呼ぶので、公開APIを止めていると使えません。

できないことと、その代わり

できないこと代わりの手
n8nが落ちたことをn8nの中で知らせる外部の死活監視サービスで /healthz/readiness を見る
手動実行の失敗をエラー用ワークフローで受ける定期実行と「Stop and Error」の試験用ワークフローで試す
CLIのバックアップだけで丸ごと戻すデータフォルダを丸ごと保存する
無料のセルフホストで2段階認証を全員に強制する各自に設定してもらい、毎月のユーザー点検で確かめる
古い版へ番号だけで戻す更新前のデータフォルダごと戻す
止まっていた間の定期実行を後から全部流す止まっていた時間帯を確かめ、必要な分を手で流す

よく詰まるところ

症状原因直し方
試しにエラーを起こしても通知が来ない手動実行の失敗ではError Triggerが動かない定期実行で失敗させて試す
通知の「内容」が空トリガーで失敗し execution.error が無いtrigger.error も読む式にする
通知に実行の画面のリンクが無い失敗した実行を保存しない設定「Save failed production executions」を保存にする
一部のワークフローだけ通知が来ない「Error Workflow」が未設定新しく作ったら毎回Settingsで指定する
止まっているのに実行一覧に何も無いトリガーの起動時に失敗している毎朝の生存確認と「Force Reconnect Every Minutes」
問い合わせが急に来なくなった証明書が切れ、フォームからの送信が弾かれている証明書を更新し、外部監視に残り日数を足す
履歴を消してもディスクが空かないSQLiteは消した分を使い回すだけ一度だけ詰め直して起動する
戻したデータに直前の変更が無いWALファイルが抜けているn8nを止めてフォルダごと保存する
戻したらCredentialが全部開けない暗号鍵が本番と違う本番と同じ N8N_ENCRYPTION_KEY で起動し直す
再起動したら勝手に版が上がっていたタグを stable や latest にしているN8N_VERSION を番号で固定する

次にやること

まだVPSに立てていないなら「n8nをセルフホストで導入する:Docker ComposeでVPSに立て、独自ドメインとHTTPSで使う」から始めます。自前で運用する手間とCloudの費用を比べるなら「n8nの料金:Cloudとセルフホストの費用、商用利用の線引き」を読みます。エラー用ワークフローの操作に慣れていなければ「n8nの使い方入門:最初のワークフローを作って動かす」で基本を確かめます。外に開ける入口を整理するときは「n8nで外部サービスとつなぐ:Webhook・HTTP Request・API」が役に立ちます。

あわせて読む