n8n dey go offline for VPS? Find the real cause
n8n fit show “connection lost” even when e dey run. Learn how to separate websocket issue, restart loop, OOM kill, and dead schedule before fixing am.
Why n8n dey keep going offline: four failures, one symptom
"n8n keeps going offline" na one sentence wey cover four different failures, and each one need different fix. The editor dey show connection lost banner while the container dey run normally. The container dey restart by itself. The kernel dey kill the Node.js process because e use too much memory. Or nothing dey wrong with the process at all, and an active workflow simply never fire. If you change the wrong setting, you go spend one weekend on problem wey you never get.
So first find out which failure you get before you touch any configuration. n8n dey run as one Node.js process, usually inside one Docker container, behind reverse proxy wey dey terminate TLS (transport layer security). Each layer fit fail for im own way, and browser dey report all of dem with the same message.
Diagnose am for this order
Run these commands for the VPS (virtual private server), then read the values wey your own machine print. No compare dem with numbers from forum thread. The values wey matter here describe your box, no be another person own.
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-streamThe STATUS column from docker ps -a tell you how long the container don dey for its current state. Compare am with the time your problem start. If the container don dey up since long before the banner show, n8n no ever go offline. Na the connection between your browser and backend break, and the next section cover this websocket path.
RestartCount na how many times Docker don restart this container. Write the number down, wait one minute, then read am again. If the number dey increase while you dey watch, na restart loop. The log lines just before each restart go show the reason.
OOMKilled na true or false flag. True mean say Linux kernel kill the process because e pass memory limit, either the container own limit or the whole machine own. This one field separate memory kill from every other type of exit, so read am before you guess.
ExitCode na the exit value wey your container last use. You no need memorise wetin every code mean. Read your own value, then read the end of docker logs from the same timestamp. The log tail and the out of memory flag together tell you wetin happen, but either one alone fit mislead you.
docker stats show current memory use beside the limit wey dey apply. Leave am running for another terminal, trigger the workflow wey dey cause the problem, then watch how the number change while the failure dey happen.
The connection lost banner usually mean na your reverse proxy
The n8n editor dey keep one long-lived push connection open to the backend so e fit stream execution progress enter the canvas. By default, that connection na WebSocket, wey N8N_PUSH_BACKEND dey select, and the default value na websocket. WebSocket dey start as normal HTTP request wey carry the headers Connection: Upgrade and Upgrade: websocket. The server dey answer 101 Switching Protocols, and from that point both sides dey use the same TCP socket for both directions.
Two things fit break this, and both dey happen for proxy, no be for n8n. The proxy fit speak HTTP/1.0 upstream or remove the upgrade headers, so the upgrade no go happen and the editor go reconnect forever. Or the upgrade fit succeed, but the proxy later close the socket because e don quiet. WebSocket wey no get message dey look exactly like idle connection. For both cases, the container dey healthy. The banner na browser dey tell you say e lose the channel.
Confirm this for browser before you edit anything. Open developer tools, go Network tab, filter to WS, then reload the editor. The push request suppose reach 101 Switching Protocols and remain open. If the push request return ordinary status code, or e dey show again every few seconds, na proxy most likely dey cause the problem.
nginx settings wey go keep the editor connected
nginx no dey forward upgrade unless you tell am make e do so. Connection and Upgrade na hop-by-hop headers wey nginx removes as request dey pass through, so you need put both back. The upstream protocol depend on your nginx release: from 1.29.7 (March 2026) proxy_pass dey talk HTTP/1.1 to the backend by default, but before that e dey use HTTP/1.0, wey no fit carry upgrade at all. Run nginx -v to see the one wey you get. The config below keep proxy_http_version 1.1 explicit so e go work for both, and for 1.29.7 or later, that line just dey repeat the default. Put the map block inside http context, no be inside server. If the rest of the server block below no clear, the line-by-line walkthrough of an nginx server block explain wetin each directive dey do.
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 na the line wey people dey leave out. Its default na 60 seconds, and e apply to upgraded WebSocket too. So, if editor tab dey open for instance wey no dey get traffic, e go lose connection about one minute after the last message pass through. Increase am to fix the banner wey go show when you return to tab wey you leave open.
sudo nginx -t && sudo systemctl reload nginx
sudo nginx -T | grep -iE 'proxy_http_version|upgrade|proxy_read_timeout'nginx -T dey print the complete running configuration instead of just one file, so e prove say your edit really load. If config dey inside file wey no include line pick up, na that one make correct fix look like say e no do anything.
Then tell n8n say e dey behind proxy, because e dey build URLs from these values.
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 default na 0. This means n8n dey treat the connecting address as the client address and e ignore X-Forwarded-For. Set am to the number of proxies wey dey in front of the container. As of October 2026, N8N_WEBHOOK_URL na the current name, while the older WEBHOOK_URL still dey work but e go print deprecation warning when startup happen.
Traefik dey forward websockets, then timeout dem
Traefik dey forward WebSocket upgrade without middleware or extra labels, so when Traefik user see this banner, na usually timeout e dey face, no be missing header. The settings dey for entryPoint. As of October 2026 for Traefik v3, idleTimeout default na 180 seconds and readTimeout default na 60 seconds.
entryPoints:
websecure:
address: ":443"
transport:
respondingTimeouts:
readTimeout: 0
idleTimeout: 3600sCaddy dey handle the upgrade automatically for reverse_proxy and e no need any directive for am. If you no fit change the proxy at all because another person own am, change the push channel with N8N_PUSH_BACKEND=sse. SSE (server-sent events) na normal HTTP response wey stay open, so e fit continue through proxy wey no allow upgrades, though aggressive idle timeout still fit cut am. Choosing the proxy na separate decision, and the nginx, Caddy and Traefik comparison explain wetin each one go cost you to operate.
When the container dey restart for real
If RestartCount dey climb, the container dey fail and Docker dey bring am back. Match the log timestamps with each restart and read wetin happen immediately before each one. Four causes dey cover almost everything: configuration error wey stop startup, database wey n8n no fit reach, crash after e don start, and memory kill.
Start with the volume, because na permissions dey cause this quietly. The official image dey run as unprivileged user node and e dey keep data for /home/node/.n8n. Bind mount wey root create no dey writable by that user, so the process dey die for startup every time and the restart policy dey hide the problem inside a loop.
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 go avoid the problem completely, because Docker dey create am with the correct ownership. If you need bind mount, chown the host directory to the numeric user id wey the first command print. E good make you understand ownership mapping between host and container once, and the PUID and PGID explanation explain how these images dey decide who fit write the files.
The out of memory kill wey look like crash
There are two separate memory ceilings above an n8n process, and dem dey fail differently. The container control group limit na kernel dey enforce am: cross am and kernel go kill the process immediately, with no chance to write anything, and OOMKilled go read true. V8 heap limit na Node.js dey enforce am inside: cross am and Node go throw heap error with stack trace, then exit by itself, so OOMKilled go read false. From browser, both one look. From docker inspect, dem dey differ by one field.
Set Node heap ceiling below container limit. If heap ceiling higher pass the two, V8 go continue allocate memory past the point wey kernel go step in. That one mean say garbage collector no go reach its own limit, and you go always get the harsher failure with no log to read.
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>Pick both numbers based on wetin your VPS actually get, and leave room for database, proxy, and operating system. docker stats --no-stream prints current use beside the limit wey dey apply, so you fit confirm say the limit wey you write na the same limit Docker apply. How Compose memory limits dey apply explains which key go win when several ones dey set.
Execution data na wetin dey grow under you
One execution dey hold the output of every node while the run dey happen, and n8n later stores that data. Two things follow from this. The peak memory for one run dey depend on the biggest batch of data wey you push through am. So, workflow wey dey handle ten thousand rows at once no be the same program as that workflow when e dey handle two hundred rows per time. The stored copy too go continue to grow until something delete am.
Pruning dey handle the second problem. As of October 2026, the defaults be say pruning dey enabled, EXECUTIONS_DATA_MAX_AGE dey set to 336 hours (14 days), and EXECUTIONS_DATA_PRUNE_MAX_COUNT dey set to 10000. Those values plenty for small VPS wey dey run SQLite, where one file dey hold everything and the same process wey dey serve the editor still need read and write the file.
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 na the aggressive setting. E dey keep failed executions for debugging and throw away successful ones. Decide this intentionally, because if workflow produce wrong output without raising error, you no go get anything to inspect later. Pruning first mark rows as deleted and remove dem during a later pass. SQLite dey reuse pages wey don free instead of returning dem, so the file for disk no go shrink immediately after you change the setting.
To reduce the peak instead of the stored total, move less data for each run. Split large jobs into sub-workflows wey return small results to the parent, use the Loop Over Items node for batching, and no keep complete datasets inside the Code node.
Binary file no suppose dey pass through memory
N8N_DEFAULT_BINARY_DATA_MODE default na default, wey dey keep binary data for memory of the execution wey dey run. Every file wey one node download, and every copy wey e hand over to the next node, dey remain there until the run end. One workflow wey dey fetch some big attachments fit push the process pass limit wey ordinary JSON work no dey near. Na why crash dey follow one particular workflow instead of time.
environment:
- N8N_DEFAULT_BINARY_DATA_MODE=filesystemWith filesystem, e dey write binary data under N8N_BINARY_DATA_STORAGE_PATH. By default, this folder dey inside n8n user folder, so e go land for the same volume with everything else. Check say the volume get enough space before you switch. N8N_PAYLOAD_SIZE_MAX set the biggest incoming webhook payload for MiB (mebibytes) and default na 16. If you raise am, bigger requests fit enter, but na memory cost wey you choose to accept.
Anything else wey dey share the box dey compete for the same RAM. If OOM kills start after you add database container, whether you run the database for Docker or for the host na the trade-off wey you dey make now.
Restart policy, and how e go come back after reboot
Container wey no get restart policy go remain stopped after e exits and after host reboot. restart: unless-stopped go bring am back for both cases, but e still respect container wey you stop by hand. restart: always go also restart container wey you stop deliberately, once Docker start again.
n8n get health endpoint. N8N_ENDPOINT_HEALTH na the name for this endpoint, and e default to healthz. Check am from host first, so you fit confirm say the path correct for your instance.
curl -fsS http://127.0.0.1:5678/healthz
docker exec n8n which wget curl
sudo systemctl is-enabled dockerHealthcheck by itself no restart anything. Compose go mark the container as unhealthy and stop there. So, healthcheck need restart policy or external watcher beside am before e fit get any effect. How to write healthcheck wey go really act and how to make the stack start again after reboot cover both parts.
Workflow wey no dey ever fire even though n8n dey okay
This one no dey show banner and no dey restart. The container dey up, the editor dey work, but the run wey you expect no dey for executions list. Four causes dey account for most cases.
- Workflow no active. Schedule Trigger only dey run for production path, so testing am for canvas no dey schedule anything.
- Timezone no be your own.
GENERIC_TIMEZONEdey default toAmerica/New_York, so schedule wey set for 09:00 go fire for 09:00 for that zone until you setGENERIC_TIMEZONEandTZto your own. - Downtime no dey make missed run happen later. Triggers register when n8n starts, so schedule wey due while container dey restart no go run late. Next run na the next due time after startup.
- Workflow deactivate itself.
N8N_WORKFLOW_AUTODEACTIVATION_ENABLEDdey off by default. When e dey on, workflow wey continue to crash go get unpublished, and e go look exactly like workflow wey nobody ever activate.
Open executions list and filter am to that workflow. Entry wey fail na workflow problem. If e fail with 429 against another service wey you host for the same box, the limit belong to that service, no be n8n. The SearXNG 429 walkthrough show how to separate that service own rate limiter from engines wey dey block your server IP. If no entry dey at all, na trigger problem. Check the four causes above first.
Wetin to change first
- Read
STATUS,RestartCountandOOMKilledfor your own container before you edit any file. - If the container never go down, fix the proxy upgrade headers and the idle timeout.
- If
OOMKilledna true, set a container limit wey you choose deliberately, put the Node heap ceiling below am, and switch binary data tofilesystem. - If nothing trigger, check say the workflow dey active and the instance timezone na your own.
Most of this na configuration wey you set once and forget, on top of working install. If you still dey put that install together, the n8n on Docker with HTTPS walkthrough na the base wey these settings belong to.
FAQ
Why n8n editor dey show connection lost banner when container dey run?
The editor dey keep one WebSocket open to stream execution progress. If your reverse proxy no forward the Connection: Upgrade and Upgrade: websocket headers, or e no use HTTP/1.1 upstream, the upgrade no go complete and browser go dey reconnect forever while n8n remain healthy. For nginx, you need both proxy_set_header lines, proxy_http_version 1.1 for releases wey older than 1.29.7 (newer ones dey use HTTP/1.1 by default), and one proxy_read_timeout wey pass the default 60 seconds so quiet tab no cut off. Check the running config with sudo nginx -T, no be the file wey you edit.
How I fit know out of memory kill from ordinary crash?
Run docker inspect n8n | grep -iE 'OOMKilled|ExitCode|RestartCount' and read the OOMKilled flag. True mean say kernel kill the process because e pass memory limit, and you no go see anything useful for container log because process no get chance to write. False, together with heap error and stack trace for the end of docker logs, mean say Node.js reach im own V8 heap ceiling and exit by itself. Set NODE_OPTIONS=--max-old-space-size below your container limit so you go get the second failure, because na that one dey leave evidence.
Pruning execution data dey free disk space immediately?
No. EXECUTIONS_DATA_PRUNE mark old executions for deletion, then later pass go remove dem, according to the schedule wey EXECUTIONS_DATA_PRUNE_HARD_DELETE_INTERVAL set. With SQLite, the file also reuse freed pages instead of returning dem to filesystem, so the size for disk go remain the same for some time after the rows don go. Set EXECUTIONS_DATA_MAX_AGE and EXECUTIONS_DATA_PRUNE_MAX_COUNT to values wey suit your box, then check again the next day instead of immediately.
Why my scheduled workflow no run while n8n dey restart?
n8n register triggers when process start, and e no replay schedules wey become due while e dey down. So restart loop go produce silence instead of burst of catch-up runs, and the next execution go be the next due time after startup. If you need runs wey no fit miss, make external caller hit a webhook to drive the workflow, so retry logic dey outside n8n.
Healthcheck go restart n8n when e stop responding?
No be by itself. Compose healthcheck only mark the container as healthy or unhealthy. Restart policy na the one wey handle restarting, so restart: unless-stopped na wetin go bring the container back after e exit, and e also go bring am back after host reboot as long as Docker service dey enabled. Confirm am with sudo systemctl is-enabled docker. If you want act specifically when e unhealthy, you need watcher outside Docker wey go read the status and restart the service.