n8n VPS 持續離線?4 種故障診斷方法
n8n 顯示「連線中斷」不一定真的離線。本文教你分辨 WebSocket 反向代理問題、容器重新啟動迴圈、out of memory 終止,以及排程未觸發。
n8n 為何持續離線:4 種故障、1 種症狀
「n8n 持續離線」這句話可能涵蓋 4 種不同故障,而每種故障都需要不同的修正方式。編輯器顯示連線中斷橫幅,但容器實際上正常執行。容器自行重新啟動。核心因 Node.js 使用過多記憶體而終止該程序。或者程序完全沒有問題,只是啟用中的工作流程始終未觸發。若修改了錯誤的設定,可能會花上一個週末處理一個原本不存在的問題。
因此,在修改任何設定前,先確認是哪一種故障。n8n 會以單一 Node.js 程序執行,通常位於單一 Docker 容器中,並置於負責 TLS termination(傳輸層安全性)的反向代理後方。每一層都有各自的故障模式,而瀏覽器會以相同訊息回報所有這些故障。
依此順序診斷
在 VPS(virtual private server)上執行以下命令,並查看自己機器輸出的值。不要拿論壇文章中的數字來比較。這裡重要的值描述的是你的主機,不是其他人的主機。
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-streamdocker ps -a 的 STATUS 欄位表示容器目前狀態已持續多久。將這個時間與問題開始的時間比較。如果容器在提示訊息出現前很久就已持續執行,表示 n8n 從未離線。發生問題的是瀏覽器與後端之間的連線,也就是下一節說明的 websocket 路徑。
RestartCount 表示 Docker 重新啟動此容器的次數。記下這個數字,等待一分鐘後再讀取一次。如果你觀察期間數字持續增加,表示容器陷入重新啟動迴圈。每次重新啟動前的日誌內容會記錄原因。
OOMKilled 是 true 或 false 旗標。True 表示 Linux kernel 因程序超過記憶體限制而將其終止。限制可能來自容器本身,也可能來自整台機器。這個欄位可區分記憶體終止與其他類型的結束,因此應先讀取它,再進行推測。
ExitCode 是容器上次結束時傳回的值。你不需要記住每個代碼的含義。先讀取自己的值,再查看相同時間戳記的 docker logs 結尾內容。日誌尾端與 out of memory 旗標必須一起判讀,兩者單獨使用都可能造成誤判。
docker stats 會顯示目前的記憶體使用量,以及目前套用的限制。讓它在第二個終端機中持續執行,觸發造成問題的工作流程,並觀察失敗發生時數值的變化。
「連線中斷」橫幅通常表示反向代理發生問題
n8n 編輯器會對後端維持一條長時間開啟的推送連線,將執行進度串流到畫布。這條連線預設使用 WebSocket,由 N8N_PUSH_BACKEND 選取,預設值為 websocket。WebSocket 會先以一般 HTTP 要求建立,並攜帶 Connection: Upgrade 與 Upgrade: websocket 標頭。伺服器回覆 101 Switching Protocols 後,雙方就會透過同一個 TCP socket 進行雙向傳輸。
有兩種情況會導致連線中斷,而且問題都出在代理層,不在 n8n。代理可能以 HTTP/1.0 與上游服務通訊,或移除 upgrade 標頭,導致升級無法完成,編輯器便會不斷重新連線。另一種情況是升級成功,但代理之後因 socket 長時間沒有資料而將其關閉,因為沒有訊息的 WebSocket 看起來就像閒置連線。這兩種情況下,容器都處於正常狀態。橫幅只是瀏覽器告知你推送通道已中斷。
在修改任何設定前,先於瀏覽器確認問題。開啟開發人員工具,前往 Network 分頁,篩選 WS,然後重新載入編輯器。推送要求應該能到達 101 Switching Protocols,並維持開啟狀態。如果推送要求回傳一般狀態碼,或每隔幾秒重新出現一次,問題通常出在代理層。
維持 editor 連線的 nginx 設定
nginx 不會在未明確要求時轉送 upgrade。proxy_pass 預設會使用 HTTP/1.0 與後端通訊,而 Connection 和 Upgrade 屬於 hop-by-hop headers,nginx 轉送時會移除。您必須將兩者加回去。map 區塊應放在 http context 中,不可放在 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 seconds,且同樣套用至已升級的 WebSocket。因此,若 editor 分頁保持開啟,而該 instance 沒有流量,連線會在最後一則訊息通過後約 1 分鐘中斷。提高此值即可修正您返回長時間開啟的分頁時看到的提示。
sudo nginx -t && sudo systemctl reload nginx
sudo nginx -T | grep -iE 'proxy_http_version|upgrade|proxy_read_timeout'nginx -T 會輸出完整的執行中設定,而不是單一檔案,因此可確認您的修改確實已載入。若設定檔未被 include 指令讀取,即使修正內容正確,也會看起來沒有作用。
接著告訴 n8n 它位於 proxy 後方,因為 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 會將連入位址視為 client 位址,並忽略 X-Forwarded-For。請將其設為 container 前方的 proxy 數量。截至 August 2026,N8N_WEBHOOK_URL 是目前使用的名稱;舊名稱 WEBHOOK_URL 仍可運作,但啟動時會輸出 deprecation warning。
Traefik 轉送 WebSocket,但之後逾時
Traefik 不需 middleware 或額外 labels,就能轉送 WebSocket upgrade。因此,Traefik 使用者看到此訊息時,通常遇到的是逾時,而不是缺少標頭。相關設定位於 entryPoint。在 2026 年 8 月的 Traefik v3 中,idleTimeout 的預設值為 180 秒,readTimeout 的預設值為 60 秒。
entryPoints:
websecure:
address: ":443"
transport:
respondingTimeouts:
readTimeout: 0
idleTimeout: 3600sCaddy 會在 reverse_proxy 自動處理 upgrade,不需要額外指令。如果完全無法變更 proxy,因為它由其他人管理,請使用 N8N_PUSH_BACKEND=sse 切換推送通道。SSE(server-sent events)是保持連線的標準 HTTP 回應,因此即使 proxy 拒絕 upgrade,仍可正常運作;但過短的 idle timeout 仍會中斷連線。選擇 proxy 本身是另一項決策,nginx、Caddy 與 Traefik 的比較說明了各方案的維運成本。
容器確實持續重新啟動時
如果 RestartCount 持續上升,表示容器發生故障,而 Docker 正在將它重新啟動。比對日誌時間戳與每次重新啟動的時間,查看重新啟動前緊接著出現的內容。幾乎所有情況都可歸納為 4 個原因:阻止啟動的設定錯誤、n8n 無法連線的資料庫、服務執行後發生當機,以及記憶體終止程序。
先檢查 volume,因為權限問題最不容易察覺。官方映像檔會以非特權使用者 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/.n8nnamed volume 可完全避免這個問題,因為 Docker 會以正確的擁有權建立它。如果需要使用 bind mount,請將主機目錄的擁有權 chown 為第一個命令輸出的數值 user id。了解主機與容器之間的擁有權對應方式很有幫助,PUID 與 PGID 說明會介紹這些映像檔如何決定誰能寫入檔案。
看似當機的記憶體不足終止
n8n 程序上方有兩個彼此獨立的記憶體上限,失效方式也不同。容器 control group 的限制由 kernel 強制執行:超過限制後,程序會立即被終止,沒有機會寫入任何內容,而且 OOMKilled 會讀取為 true。V8 heap 的限制則由 Node.js 內部執行:超過限制後,Node 會擲出 heap 錯誤,顯示 stack trace,然後自行結束,因此 OOMKilled 會讀取為 false。從瀏覽器看,這兩種情況完全相同。從 docker inspect 看,兩者只差一個欄位。
將 Node heap 上限設在容器限制以下。如果 heap 上限較高,V8 會持續配置記憶體,超過 kernel 介入的臨界點。因此,其 garbage collector 永遠不會達到自身的限制,最後一律發生較嚴重的失效,而且沒有 log 可供查看。
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>根據 VPS 實際可用的資源設定這兩個數值,並為資料庫、代理伺服器與作業系統保留空間。docker stats --no-stream 會在目前使用量旁顯示現行限制,讓你確認自行設定的限制就是 Docker 套用的限制。Compose 如何套用記憶體限制 詳細說明同時設定多個限制時,哪個 key 會生效。
執行資料會持續累積
單次執行會在流程進行期間保留每個節點的輸出,n8n 隨後會儲存這些資料。這會帶來兩個結果。單次執行的記憶體峰值取決於其中傳遞的最大資料批次,因此,一次處理一萬列資料的工作流程,和每次處理兩百列資料的相同工作流程,實際上是不同的程式。儲存的副本則會持續增加,直到有程序將其刪除。
Pruning 可處理第二個問題。截至 2026 年 8 月,預設值為啟用 pruning、EXECUTIONS_DATA_MAX_AGE 設為 336 小時(14 天),以及 EXECUTIONS_DATA_PRUNE_MAX_COUNT 設為 10000。對於使用 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=falseEXECUTIONS_DATA_SAVE_ON_SUCCESS=none 是積極的設定。它會保留失敗的執行結果以便偵錯,並捨棄成功的執行結果。請刻意決定是否採用此設定,因為工作流程可能在未產生錯誤的情況下輸出錯誤資料,之後便沒有任何內容可供檢查。Pruning 也會先將資料列標記為已刪除,再於後續處理中移除;而 SQLite 會重新使用釋出的頁面,不會將其歸還給檔案系統,因此變更設定後,磁碟上的檔案不會立即縮小。
若要降低峰值,而不是減少儲存總量,請減少每次執行傳遞的資料量。將大型工作拆分為可回傳少量結果給父工作流程的子工作流程,使用 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 payload 的大小上限,單位為 MiB (mebibytes),預設值為 16。提高此上限可接受更大的請求,但也代表你選擇承擔相應的記憶體成本。
同一台主機上的其他服務也會競用相同的 RAM。如果 OOM kill 是在你新增資料庫容器後開始發生,在 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 只會將容器標記為不健康並停止處理,因此健康檢查必須搭配重新啟動原則,或由外部監控程式配合,才會產生實際作用。撰寫能實際發揮作用的健康檢查 和 讓堆疊在重新開機後再次啟動 分別說明這兩個部分。
n8n 正常運作,但工作流程始終不會觸發
這個工作流程不會顯示橫幅,也不會重新啟動。容器正在執行,編輯器可以正常使用,但你預期的執行記錄沒有出現在執行清單中。大多數情況都可歸因於以下 4 個原因。
- 工作流程未啟用。Schedule Trigger 只會在 production 路徑中執行,因此在畫布中測試不會建立排程。
- 時區不是你使用的時區。
GENERIC_TIMEZONE預設為America/New_York,因此排程設為 09:00 時,會先以該時區的 09:00 觸發,直到你將GENERIC_TIMEZONE和TZ設為自己的時區。 - 停機期間錯過的觸發不會事後補執行。n8n 啟動時才會註冊觸發器,因此容器重新啟動期間到期的排程不會延後執行。下一次執行時間是啟動後下一個到期時間。
- 工作流程被系統停用。
N8N_WORKFLOW_AUTODEACTIVATION_ENABLED預設為關閉;啟用後,持續崩潰的工作流程會被取消發布,之後看起來就像從未啟用過一樣。
開啟執行清單,並篩選該工作流程。若有失敗的記錄,問題出在工作流程本身。若完全沒有記錄,問題出在觸發器,應從上述 4 個原因著手檢查。
優先變更的項目
- 在編輯任何檔案前,先自行讀取容器中的
STATUS、RestartCount和OOMKilled。 - 如果容器從未停止,請修正代理伺服器的升級標頭和閒置逾時設定。
- 如果
OOMKilled為 true,請刻意設定容器限制,將 Node heap 上限設在該限制以下,並將二進位資料切換至filesystem。 - 如果沒有任何項目觸發,請確認工作流程處於啟用狀態,且執行個體時區設定為你的時區。
這些大多是安裝完成後設定一次即可長期維持的組態。如果你仍在建置該安裝環境,n8n on Docker with HTTPS 操作指南是這些設定所依據的基礎。
FAQ
為什麼容器正在執行時,n8n 編輯器仍顯示連線中斷提示?
編輯器會維持 WebSocket 連線,以串流傳送執行進度。如果反向代理未轉送 Connection: Upgrade 和 Upgrade: websocket 標頭,或上游未使用 HTTP/1.1,升級程序就不會完成。此時瀏覽器會持續重新連線,但 n8n 仍正常運作。在 nginx 中,您需要 proxy_http_version 1.1 以及兩行 proxy_set_header,並將 proxy_read_timeout 設定為長於預設 60 秒,避免閒置分頁被中斷。請使用 sudo nginx -T 檢查目前執行中的設定,而不是檢查您編輯過的檔案。
如何判斷程序是因記憶體不足遭終止,還是一般當機?
執行 docker inspect n8n | grep -iE 'OOMKilled|ExitCode|RestartCount',並查看 OOMKilled 旗標。True 表示核心因程序超過記憶體限制而將其終止。此時容器日誌通常沒有有用資訊,因為程序沒有機會寫入日誌。若該值為 False,且 docker logs 結尾出現 heap 錯誤與 stack trace,表示 Node.js 觸及自身的 V8 heap 上限,並自行結束。將 NODE_OPTIONS=--max-old-space-size 設定為低於容器限制的值,這樣會得到第二種失敗結果,而這種結果會留下診斷資訊。
清理執行資料會立即釋放磁碟空間嗎?
不會。EXECUTIONS_DATA_PRUNE 會將舊的執行資料標記為待刪除,之後才會依 EXECUTIONS_DATA_PRUNE_HARD_DELETE_INTERVAL 設定的排程執行清理。使用 SQLite 時,資料庫檔案也會重複使用已釋放的頁面,而不是將空間歸還給檔案系統。因此,資料列刪除後,磁碟上的檔案大小仍可能暫時維持不變。請將 EXECUTIONS_DATA_MAX_AGE 和 EXECUTIONS_DATA_PRUNE_MAX_COUNT 設定為適合您主機的值,並在隔天再次檢查,不要立即檢查。
為什麼 n8n 重新啟動期間,我排程的工作流程沒有執行?
n8n 會在程序啟動時註冊觸發條件,不會補執行停機期間已到期的排程。因此,反覆重新啟動只會造成工作流程沒有執行,不會在恢復後一次執行大量補跑工作。下一次執行時間會是啟動後的下一個到期時間。如果執行不可遺漏,請改由外部呼叫端呼叫 webhook 來觸發工作流程,讓重試邏輯位於 n8n 外部。
healthcheck 會在 n8n 停止回應時重新啟動 n8n 嗎?
不會自動重新啟動。Compose healthcheck 只會將容器標記為 healthy 或 unhealthy。重新啟動由 restart policy 負責,因此 restart: unless-stopped 會在容器結束後將其啟動;只要 Docker 服務已啟用,主機重新開機後也會重新啟動容器。請使用 sudo systemctl is-enabled docker 確認此設定。若要針對 unhealthy 狀態採取動作,必須在 Docker 外部執行 watcher,讀取容器狀態並重新啟動服務。