SSD Nodes Learn 🎉 VPS $5.50/月〜
ガイド Matt Connor著者 Matt Connor

n8nがVPSでオフラインになる4つの原因と対処法

n8nのオフライン表示は、websocketの接続断、再起動ループ、メモリ不足による強制終了、スケジュール停止の4種類です。Dockerの再起動回数やOOMKilledを確認し、原因を切り分けます。

n8n がオフラインになる理由: 4 つの障害と 1 つの症状

「n8n がオフラインになり続ける」という表現は、異なる 4 つの障害をまとめて指しています。それぞれ必要な対処が異なります。コンテナが正常に動作しているにもかかわらず、エディターには接続が失われたことを示すバナーが表示される場合があります。コンテナが自動的に再起動する場合もあります。メモリを使い過ぎたために、カーネルが Node.js プロセスを強制終了する場合もあります。また、プロセスには問題がなく、実行中のワークフローがまったく起動しないだけの場合もあります。誤った設定を変更すると、実際には存在しない問題に週末を費やすことになります。

そのため、設定に触れる前に、どの障害が発生しているかを特定してください。n8n は単一の Node.js プロセスとして動作し、通常は TLS (transport layer security) を終端するリバースプロキシの背後で、1 つの Docker コンテナ内に配置されます。これらの各層はそれぞれ異なる原因で停止し、ブラウザーはすべて同じメッセージで報告します。

この順序で診断します

VPS(仮想プライベートサーバー)上で次のコマンドを実行し、自分のマシンが出力する値を確認します。フォーラムの投稿にある数値と比較しないでください。ここで重要なのは、他人の環境ではなく、使用しているサーバーの状態を示す値です。

docker ps -a --filter name=n8n
docker logs --tail 200 --timestamps n8n
docker inspect n8n | grep -iE 'Status|Running|RestartCount|OOMKilled|ExitCode'
docker stats --no-stream

docker ps -aSTATUS 列は、コンテナが現在の状態になってからの経過時間を示します。問題が発生した時刻と比較してください。バナーが表示されるよりずっと前からコンテナが起動している場合、n8n は停止していません。問題は、ブラウザーとバックエンドの接続で発生しています。これは次のセクションで扱う websocket の経路です。

RestartCount は、Docker がこのコンテナを再起動した回数です。数値を記録し、1 分待ってからもう一度確認します。監視中に数値が増える場合は、再起動ループが発生しています。各再起動の直前に出力されたログ行に原因が記録されています。

OOMKilled は true または false のフラグです。True の場合、コンテナ自身のメモリ制限、またはマシン全体のメモリ制限を超えたため、Linux カーネルがプロセスを終了しています。このフィールドだけで、メモリ不足による終了と、それ以外の終了を区別できます。そのため、推測する前に確認します。

ExitCode は、コンテナが最後に終了したときの終了コードです。各コードの意味を暗記する必要はありません。自分の値を確認し、同じタイムスタンプにおける docker logs の末尾も確認してください。ログの末尾と out of memory フラグを組み合わせると何が起きたかを判断できます。どちらか一方だけでは誤った判断につながることがあります。

docker stats は、現在適用されている制限とともに、使用中のメモリ量をリアルタイムで表示します。別のターミナルで実行したままにし、問題が発生するワークフローを実行してください。障害が発生している間に数値がどう変化するかを確認します。


「接続が失われました」バナーの原因は、通常リバースプロキシです

n8n エディターは、実行の進行状況をキャンバスにストリーミングするため、バックエンドへの長時間維持される push 接続を 1 本開いたままにします。デフォルトでは、この接続に WebSocket を使用します。N8N_PUSH_BACKEND がこれを選択し、デフォルト値は websocket です。WebSocket は、Connection: UpgradeUpgrade: websocket のヘッダーを含む通常の HTTP リクエストとして開始されます。サーバーは 101 Switching Protocols で応答し、その後は双方が同じ TCP ソケットを双方向で使用します。

この接続が切れる原因は 2 つあり、どちらも n8n ではなくプロキシ側にあります。プロキシが上流との通信に HTTP/1.0 を使用するか、upgrade ヘッダーを削除すると、アップグレードが実行されず、エディターは再接続を繰り返します。あるいは、アップグレードが成功した後、通信がないためにプロキシがソケットを閉じることもあります。メッセージのない WebSocket は、アイドル状態の接続と同じように見えるためです。どちらの場合も、コンテナ自体は正常です。バナーは、ブラウザーが接続チャネルを失ったことを示しています。

設定を変更する前に、ブラウザーでこの状況を確認してください。開発者ツールを開き、Network タブで WS に絞り込んでから、エディターを再読み込みします。push リクエストは 101 Switching Protocols に到達し、接続を開いたままにする必要があります。通常のステータスコードを返す push リクエストや、数秒おきに再表示されるリクエストは、プロキシ側に問題があることを示します。

エディターの接続を維持する nginx 設定

nginx は、明示的に指定しない限り upgrade を転送しません。proxy_pass はデフォルトでバックエンドと HTTP/1.0 で通信し、ConnectionUpgrade は nginx が転送時に削除する hop-by-hop ヘッダーです。両方を再設定する必要があります。map ブロックは http コンテキストに配置し、server の内部には配置しません。

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}
server {
    listen 443 ssl;
    http2 on;
    server_name n8n.example.com;

    location / {
        proxy_pass http://127.0.0.1:5678;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
        proxy_buffering off;
    }
}

proxy_read_timeout は記述漏れが多い行です。デフォルト値は 60 秒で、upgrade 後の WebSocket にも適用されます。そのため、静かなインスタンスでエディターのタブを開いたままにすると、最後のメッセージが通過してから約 1 分後に接続が失われます。値を大きくすると、開いたままにしていたタブへ戻ったときに表示されるバナーを解消できます。

sudo nginx -t && sudo systemctl reload nginx
sudo nginx -T | grep -iE 'proxy_http_version|upgrade|proxy_read_timeout'

nginx -T は 1 つのファイルではなく、実行中の設定全体を出力します。そのため、編集内容が実際に読み込まれていることを確認できます。正しい設定を記述しても効果がない場合は、その設定が include 行で読み込まれるファイルに含まれていないことが原因です。

次に、n8n にプロキシ配下で動作していることを伝えます。n8n はこれらの値から URL を生成するためです。

environment:
  - N8N_HOST=n8n.example.com
  - N8N_PROTOCOL=https
  - N8N_PORT=5678
  - N8N_PROXY_HOPS=1
  - N8N_WEBHOOK_URL=https://n8n.example.com/

N8N_PROXY_HOPS のデフォルト値は 0 です。これは、n8n が接続元アドレスをクライアントアドレスとして扱い、X-Forwarded-For を無視することを意味します。コンテナの前段にあるプロキシの数を設定してください。2026 年 8 月時点では、N8N_WEBHOOK_URL が現在の名称です。以前の WEBHOOK_URL も引き続き使用できますが、起動時に非推奨警告が表示されます。

Traefik は WebSocket を転送し、その後タイムアウトします

Traefik は、ミドルウェアや追加のラベルがなくても WebSocket のアップグレードを転送します。そのため、このバナーが表示される Traefik ユーザーは、通常、ヘッダーの不足ではなくタイムアウトに遭遇しています。設定項目は entryPoint にあります。2026 年 8 月時点の Traefik v3 では、idleTimeout のデフォルト値は 180 秒、readTimeout のデフォルト値は 60 秒です。

entryPoints:
  websecure:
    address: ":443"
    transport:
      respondingTimeouts:
        readTimeout: 0
        idleTimeout: 3600s

Caddy は reverse_proxy でアップグレードを自動的に処理するため、そのためのディレクティブは不要です。プロキシをまったく変更できない場合は、別の担当者が管理している場合など、N8N_PUSH_BACKEND=sse に切り替えてプッシュ用チャネルを変更します。SSE(server-sent events)は接続を維持する通常の HTTP レスポンスです。そのため、アップグレードを拒否するプロキシでも利用できます。ただし、アイドルタイムアウトが短く設定されていると切断されます。プロキシ自体の選択は別の判断事項です。nginx、Caddy、Traefik の比較では、それぞれの運用に必要なコストを説明しています。

コンテナが実際に再起動を繰り返している場合

RestartCountが増えている場合、コンテナが失敗し、Docker が再起動させています。ログのタイムスタンプを各再起動の時刻と照合し、その直前に出力された内容を確認します。ほとんどの場合、原因は次の4つです。起動を妨げる設定エラー、n8n が接続できないデータベース、起動後のクラッシュ、メモリ不足による強制終了です。

まずボリュームを確認します。権限の問題は気付きにくいためです。公式イメージは非特権ユーザー node として実行され、データを /home/node/.n8n に保存します。root が作成した bind mount はそのユーザーから書き込めません。そのため、プロセスは起動するたびに終了し、restart policy によって再起動ループが隠れてしまいます。

docker compose config
docker run --rm -it --entrypoint sh docker.n8n.io/n8nio/n8n -c 'id'
docker exec n8n ls -ld /home/node/.n8n

named volume を使えば、Docker が適切な所有権で作成するため、この問題を完全に回避できます。bind mount が必要な場合は、ホスト側のディレクトリの所有者を、最初のコマンドで表示された数値の user id に chown します。ホストとコンテナ間の所有権マッピングは一度理解しておく価値があります。PUID と PGID の解説では、これらのイメージがファイルの書き込みを行うユーザーをどのように決定するかを説明しています。

クラッシュに見えるメモリ不足による kill

n8n プロセスには、2 つの異なるメモリ上限があり、それぞれ異なる動作をします。コンテナの control group limit はカーネルによって適用されます。これを超えると、プロセスは即座に kill され、何も書き込む機会がありません。この場合、OOMKilled は true になります。V8 heap limit は Node.js 内部で適用されます。これを超えると、Node は stack trace 付きの heap error を出して自分で終了するため、OOMKilled は false になります。ブラウザーから見ると、これらは同じように見えます。docker inspect から見ると、1 つのフィールドだけが異なります。

Node の heap 上限は、コンテナの limit より低く設定してください。heap 上限のほうが高い場合、V8 はカーネルが介入する時点を越えて割り当てを続けます。そのため、garbage collector は自身の上限に到達せず、ログを確認できない、より厳しい失敗が常に発生します。

services:
  n8n:
    image: docker.n8n.io/n8nio/n8n
    restart: unless-stopped
    environment:
      - NODE_OPTIONS=--max-old-space-size=<MiB, below the container limit>
    deploy:
      resources:
        limits:
          memory: <your container limit>

2 つの値は、データベース、proxy、オペレーティングシステムのための余裕を残したうえで、実際に VPS にあるメモリ量から決めてください。docker stats --no-stream は現在の使用量を適用中の limit と並べて表示するため、記述した limit が Docker に適用された値と一致するか確認できます。Compose でメモリ limit が適用される仕組みでは、複数の設定がある場合にどの key が優先されるかを説明しています。

実行データは運用中に蓄積します

1 回の実行では、実行中に各ノードが生成したすべての出力を保持し、n8n がそのデータを保存します。ここから2つの結果が生じます。1 回の実行で使用するメモリのピークは、処理するデータの最大バッチサイズで決まります。そのため、1 回に 10000 行を処理するワークフローは、1 回に 200 行を処理する同じワークフローとは別のプログラムです。また、保存されたコピーは、削除されるまで増え続けます。

2つ目の問題には、プルーニングを使用します。2026 年 8 月時点のデフォルトでは、プルーニングは有効で、EXECUTIONS_DATA_MAX_AGE は 336 時間(14 日)、EXECUTIONS_DATA_PRUNE_MAX_COUNT は 10000 です。すべてのデータを1つのファイルに保持し、エディターを提供する同じプロセスがそのファイルを読み書きする、小規模な SQLite 使用の VPS では、これらは余裕のある値です。

environment:
  - EXECUTIONS_DATA_PRUNE=true
  - EXECUTIONS_DATA_MAX_AGE=72
  - EXECUTIONS_DATA_PRUNE_MAX_COUNT=1000
  - EXECUTIONS_DATA_SAVE_ON_SUCCESS=none
  - EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS=false

EXECUTIONS_DATA_SAVE_ON_SUCCESS=none は積極的に削除する設定です。デバッグ用に失敗した実行を保持し、成功した実行を破棄します。この設定は意図的に選択してください。エラーを発生させずに誤った出力を生成したワークフローでは、確認できるデータが何も残らなくなるためです。プルーニングでは、まず行を削除済みとしてマークし、後続の処理で削除します。また、SQLite は解放したページをファイルシステムへ返さずに再利用するため、設定を変更してもディスク上のファイルはすぐには小さくなりません。

保存される総量ではなくピーク使用量を減らすには、1 回の実行で移動するデータ量を減らします。大きなジョブを、親に小さな結果を返すサブワークフローへ分割します。Loop Over Items ノードでバッチ処理します。データセット全体を Code ノードへ渡さないようにします。

メモリ上でバイナリファイルを扱わないでください

N8N_DEFAULT_BINARY_DATA_MODE の既定値は default です。この設定では、実行中の処理のメモリにバイナリデータを保持します。ノードがダウンロードするすべてのファイルと、次のノードに渡されるすべてのコピーが、実行終了までメモリに残ります。数個の大きな添付ファイルを取得するだけで、通常の JSON 処理では到達しない上限をプロセスが超えることがあります。そのため、クラッシュは一定時間の経過後ではなく、特定のワークフローの実行後に発生します。

environment:
  - N8N_DEFAULT_BINARY_DATA_MODE=filesystem

filesystem を使用すると、バイナリデータは N8N_BINARY_DATA_STORAGE_PATH の下に書き込まれます。この場所は既定で n8n のユーザーフォルダー内にあるため、他のデータと同じボリュームに保存されます。切り替える前に、ボリュームに十分な空き容量があることを確認してください。N8N_PAYLOAD_SIZE_MAX は、受け付ける Webhook ペイロードの最大サイズを MiB(メビバイト)単位で設定し、既定値は 16 です。値を増やすと、より大きなリクエストを受け付けられますが、その分のメモリ消費を受け入れることになります。

同じサーバー上で動作する他のサービスも、同じ RAM を使用します。データベースコンテナを追加してから OOM による強制終了が始まった場合、データベースを Docker またはホスト上で実行することが、現在選択しているトレードオフです。

再起動ポリシーと再起動後の復帰

再起動ポリシーを設定していないコンテナは、終了後もホストの再起動後も停止したままです。restart: unless-stoppedを使用すると、手動で停止したコンテナはそのままにして、どちらの場合もコンテナを復帰させます。restart: alwaysを使用すると、意図的に停止したコンテナも、次回 Docker の起動時に再起動します。

n8n は、N8N_ENDPOINT_HEALTHで指定されるヘルスエンドポイントを提供しており、デフォルトはhealthzです。まずホストからこのエンドポイントを確認し、使用している環境でパスが正しいことを確認します。

curl -fsS http://127.0.0.1:5678/healthz
docker exec n8n which wget curl
sudo systemctl is-enabled docker

ヘルスチェックだけでは、何も再起動されません。Compose はコンテナを unhealthy としてマークするだけで処理を停止するため、ヘルスチェックを機能させるには、再起動ポリシーまたは外部ウォッチャーを併用する必要があります。実際に動作するヘルスチェックの作成再起動後にスタックを再度起動する方法で、両方の方法を説明します。

n8n は正常なのにワークフローが一度も実行されない

この場合、バナーも表示されず、再起動も行われません。コンテナは起動していてエディターも動作していますが、実行履歴には想定した実行がありません。主な原因は次の4つです。

  • ワークフローが有効になっていません。Schedule Trigger は本番パスでのみ実行されるため、キャンバスでテストしてもスケジュールは登録されません。
  • タイムゾーンが正しくありません。GENERIC_TIMEZONE のデフォルトは America/New_York です。そのため、GENERIC_TIMEZONETZ を自分のタイムゾーンに設定するまで、09:00 に設定したスケジュールはそのタイムゾーンの09:00に実行されます。
  • ダウンタイム中の実行は後から補われません。トリガーは n8n の起動時に登録されます。コンテナの再起動中に実行時刻を迎えたスケジュールは、遅れて実行されません。次回の実行は、起動後に到来する次の実行時刻です。
  • ワークフローが自動的に無効化されています。N8N_WORKFLOW_AUTODEACTIVATION_ENABLED はデフォルトで無効です。有効にすると、クラッシュを繰り返すワークフローが非公開になり、最初から有効化されていなかった場合と同じ状態に見えます。

実行履歴を開き、そのワークフローで絞り込みます。失敗したエントリがある場合は、ワークフローの問題です。エントリがまったくない場合は、トリガーの問題です。確認すべき箇所は上記の4つです。

最初に変更する項目

  1. ファイルを編集する前に、自分のコンテナ上で STATUSRestartCountOOMKilled を確認します。
  2. コンテナが停止していない場合は、プロキシのアップグレードヘッダーとアイドルタイムアウトを修正します。
  3. OOMKilled が true の場合は、意図的に決めたコンテナ上限を設定し、Node のヒープ上限をその値より低くして、バイナリデータを filesystem に切り替えます。
  4. 何も実行されなかった場合は、ワークフローが有効になっていることと、インスタンスのタイムゾーンが自分のタイムゾーンになっていることを確認します。

これらの大部分は、正常に動作しているインストール環境に対して一度設定すれば、その後は変更する必要がない項目です。まだインストール環境を構築中であれば、HTTPS 対応の Docker 上の n8n 手順が、これらの設定の基盤になります。

FAQ

n8n のエディターで、コンテナが稼働中なのに接続切断のバナーが表示されるのはなぜですか?

エディターは、実行の進行状況をストリーミングするために WebSocket 接続を維持します。リバースプロキシが Connection: UpgradeUpgrade: websocket のヘッダーを転送しない場合、または upstream に HTTP/1.1 を使用しない場合、アップグレードが完了しません。そのため、n8n が正常でもブラウザーは再接続を繰り返します。nginx では proxy_http_version 1.1proxy_set_header の2行が必要です。また、表示したままのタブが切断されないように、デフォルトの60秒より長い proxy_read_timeout を設定します。編集したファイルではなく、sudo nginx -T で実行中の設定を確認してください。

メモリ不足による kill と通常のクラッシュを見分けるにはどうすればよいですか?

docker inspect n8n | grep -iE 'OOMKilled|ExitCode|RestartCount' を実行し、OOMKilled フラグを確認します。True の場合、メモリ制限を超えたためカーネルがプロセスを kill しています。この場合、プロセスがログを書き込む機会を得ていないため、コンテナログには有用な情報が残りません。False で、docker logs の末尾に heap error とスタックトレースがある場合は、Node.js が独自の V8 heap 上限に達して自ら終了しています。NODE_OPTIONS=--max-old-space-size をコンテナのメモリ制限より低く設定してください。そうすれば、証拠が残る2つ目の障害になります。

実行データを pruning すると、すぐにディスク容量が解放されますか?

いいえ。EXECUTIONS_DATA_PRUNE は古い実行を削除対象として記録し、後続の処理で削除します。その実行タイミングは EXECUTIONS_DATA_PRUNE_HARD_DELETE_INTERVAL で設定します。SQLite では、ファイルは解放されたページを再利用し、ファイルシステムへ返却しません。そのため、行を削除した後もしばらくディスク上のサイズは変わりません。EXECUTIONS_DATA_MAX_AGEEXECUTIONS_DATA_PRUNE_MAX_COUNT を環境に合う値に設定し、直後ではなく翌日に再確認してください。

n8n の再起動中に、スケジュール済みの workflow が実行されなかったのはなぜですか?

n8n はプロセスの起動時に trigger を登録します。停止中に実行時刻を迎えたスケジュールを、後から再実行することはありません。そのため、再起動ループが発生しても catch-up 実行がまとめて発生するのではなく、何も実行されません。次の実行は、起動後に到来する次の予定時刻です。必ず実行する必要がある場合は、外部の caller から webhook を呼び出して workflow を起動してください。そうすれば、retry の処理を n8n の外部に置けます。

healthcheck によって、応答しなくなった n8n は再起動されますか?

healthcheck だけでは再起動されません。Compose の healthcheck は、コンテナを healthy または unhealthy として記録するだけです。再起動は restart policy の役割です。そのため、restart: unless-stopped によって、コンテナの終了後に再起動されます。Docker service が有効であれば、ホストの再起動後にも再起動されます。sudo systemctl is-enabled docker で確認してください。unhealthy の状態を直接処理するには、Docker 外部の watcher でステータスを読み取り、service を再起動する必要があります。