Why n8n Dey Go Offline for Your VPS
n8n fit show “connection lost” even when e dey run. Learn how to spot websocket error, restart loop, OOM kill, or schedule wey no fire.
Wetin make n8n dey go offline: four failures, one symptom
"n8n dey go offline" na one sentence wey fit mean 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 dey use too much memory. Or nothing dey wrong with the process at all, and one active workflow simply never dey fire. If you change the wrong setting, you go spend weekend dey solve problem wey no dey.
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 in its own way, but browser dey report all of dem with the same message.
Diagnose am for dis order
Run these commands for the VPS (virtual private server) and 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 dey show how long the container don dey for its current state. Compare am with the time wey your problem start. If the container don dey up since long before the banner appear, n8n never go offline. Wetin break na the connection between your browser and the backend. Na the websocket path wey the next section cover.
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 as 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. The limit fit be the container own limit or the whole machine own. This field alone separate memory kill from every other kind exit. Na why you read am before you guess.
ExitCode na the value wey your container last exit with. You no need memorise wetin each 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. Either one alone fit mislead you.
docker stats dey show live memory use beside the limit wey dey active. Leave am running for a second terminal, trigger the workflow wey dey cause the problem, and watch how the number dey change while the failure dey happen.
The connection lost banner na usually your reverse proxy
The n8n editor dey keep one long-lived push connection open to the backend, so e fit stream execution progress onto the canvas. By default, that connection na WebSocket, and na N8N_PUSH_BACKEND dey select am. The default value na websocket. WebSocket dey start as ordinary HTTP request wey carry the headers Connection: Upgrade and Upgrade: websocket. The server answer na 101 Switching Protocols, then both sides begin use the same TCP socket for both directions.
Two things fit break this, and both dey happen for proxy, no be n8n. The proxy fit speak HTTP/1.0 upstream or remove the upgrade headers. Then the upgrade no go happen, and the editor go reconnect forever. Or the upgrade fit succeed, but later the proxy close the socket because e don quiet. WebSocket wey no get messages look exactly like idle connection. For both cases, the container dey healthy. The banner na browser dey tell you say e lose its 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, proxy na likely cause.
nginx settings wey keep the editor connected
nginx no dey forward upgrade unless you tell am to. proxy_pass dey talk HTTP/1.0 to the backend by default, and Connection plus Upgrade na hop-by-hop headers wey nginx dey remove as e pass through. You need put both back. The map block dey inside http context, no be inside 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 na the line wey people dey leave out. E default na 60 seconds, and e apply to upgraded WebSocket too. So, if editor tab dey open for quiet instance, e go lose connection about one minute after the last message pass through. If you increase am, e go fix the banner wey dey 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 one file, so e prove say your edit don load. If configuration dey inside file wey no include line dey pick up, correct fix fit 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 mean say n8n dey treat the connecting address as the client address and e dey ignore X-Forwarded-For. Set am to the number of proxies wey dey in front of the container. As of August 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 forward WebSocket upgrade, then e dey time dem out
Traefik dey forward WebSocket upgrade without middleware or extra labels. So, if Traefik user see this banner, e usually mean say timeout happen, not say header dey missing. The settings dey for entryPoint. As of August 2026 for Traefik v3, idleTimeout default na 180 seconds, while 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 even when proxy no accept upgrades. But aggressive idle timeout still fit cut am off. Choosing the proxy na separate decision, and comparison of nginx, Caddy and Traefik explain wetin operating each one go cost you.
When container really dey restart
If RestartCount dey climb, container dey fail and Docker dey bring am back. Match the log timestamps with each restart and read wetin happen immediately before. Four causes cover almost everything: configuration error wey stop startup, database wey n8n no fit reach, crash after e start, and memory kill.
Start with the volume, because na permissions dey cause this quietly. 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 process dey die for startup every time and restart policy dey hide am inside 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 dey avoid the problem completely, because Docker dey create am with 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 go write the files.
The out of memory kill wey look like crash
Two separate memory ceilings dey above an n8n process, and dem fail for different ways. Kernel dey enforce the container control group limit: once process pass am, kernel kill the process immediately. Process no get chance write anything, and OOMKilled go read true. Node.js dey enforce the V8 heap limit inside itself: once process pass am, Node go throw heap error with stack trace and exit by itself, so OOMKilled go read false. For browser, both cases look the same. For docker inspect, dem dey differ by one field.
Set the Node heap ceiling below the container limit. If heap ceiling higher than container limit, V8 go continue allocate memory past the point wey kernel go step in. This mean say garbage collector no go reach im own limit, and you go always get the harsher failure without any 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>Choose both numbers based on wetin your VPS actually get, and leave space for the database, proxy, and operating system. docker stats --no-stream go print current use beside the limit wey dey active, so you fit confirm say the limit wey you write na the limit Docker apply. How Compose memory limits dey apply explain 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 store that data. Two things follow from this. The peak memory of one run depend on the biggest batch of data wey you push through am. So workflow wey handle ten thousand rows at once na different program from the same workflow wey handle two hundred at a time. And the stored copy go continue to grow until something delete am.
Pruning dey handle the second problem. As of August 2026, the defaults be pruning enabled, EXECUTIONS_DATA_MAX_AGE for 336 hours (14 days), and EXECUTIONS_DATA_PRUNE_MAX_COUNT for 10000. These settings get plenty allowance for small VPS wey dey run SQLite, where one file hold everything and the same process wey serve the editor must read and write am.
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 keep failed executions for debugging and throw away successful ones. Decide this on purpose, because if workflow produce wrong output without raising an error, you no go get anything to inspect. Pruning first mark rows as deleted and remove dem during a later pass. SQLite also reuse freed pages instead of returning dem, so the file for disk no go reduce 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 files no suppose travel 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, plus every copy wey dem hand over to the next node, dey stay there until the run finish. One workflow wey dey fetch some big attachments fit push the process pass limit wey normal JSON work never near. Na why the crash dey follow one particular workflow instead of clock time.
environment:
- N8N_DEFAULT_BINARY_DATA_MODE=filesystemWith filesystem, binary data dey write under N8N_BINARY_DATA_STORAGE_PATH. By default, this folder dey inside the n8n user folder, so e dey 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 the default na 16. If you increase am, bigger requests fit enter, but you dey choose to accept the extra memory cost.
Anything else wey dey share the same box dey compete for the same RAM. If OOM kills start after you add database container, to 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 exit, 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 dey serve health endpoint, and N8N_ENDPOINT_HEALTH name am. E default to healthz. Check am from the host first, so you go know 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 the healthcheck need restart policy or external watcher beside am before e fit get any effect. How to write healthcheck wey go actually act and How to make the stack start again after reboot cover both sides.
The workflow wey no dey ever run while n8n dey fine
This one no dey produce banner and e no dey restart. The container dey up, the editor dey work, but the run wey you expect no dey the executions list. Four causes dey responsible for most cases.
- The workflow no active. A Schedule Trigger only dey run for the production path, so testing am for the canvas no dey schedule anything.
- The timezone no be your own.
GENERIC_TIMEZONEdey default toAmerica/New_York, so schedule wey set for 09:00 go run at 09:00 for that zone until you setGENERIC_TIMEZONEandTZto your own timezone. - Downtime no dey compensate later. Triggers dey register when n8n starts, so schedule wey reach due time while the container dey restart no go run late. The next run na the next due time after startup.
- The workflow deactivate by itself.
N8N_WORKFLOW_AUTODEACTIVATION_ENABLEDdey off by default, and when e dey on, workflow wey dey crash repeatedly gets unpublished. After that, e go look exactly like workflow wey nobody ever activate.
Open the executions list and filter am to that workflow. Entry wey fail na workflow problem. If no entry dey at all, na trigger problem, and the four causes above na where you suppose check.
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 container limit wey you choose deliberately, put the Node heap ceiling below am, and switch binary data gofilesystem. - If nothing fire, check say the workflow dey active and say the instance timezone na your own.
Na configuration be most of this thing. You set am once and forget am, as long as the install dey work. If you still dey put the install together, the n8n for 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?
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 proxy_http_version 1.1 plus both proxy_set_header lines, and one proxy_read_timeout wey long 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 container log no go get anything useful 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 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, and later pass go remove dem, according to schedule wey EXECUTIONS_DATA_PRUNE_HARD_DELETE_INTERVAL set. With SQLite, the file also reuse freed pages instead of returning dem to filesystem, so size for disk go remain flat for some time after the rows don disappear. Set EXECUTIONS_DATA_MAX_AGE and EXECUTIONS_DATA_PRUNE_MAX_COUNT to values wey fit 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 next execution na the next due time after startup. If you need runs wey no fit miss, make external caller hit webhook to drive the workflow, so retry logic dey outside n8n.
Healthcheck go restart n8n when e stop responding?
Not by itself. Compose healthcheck only mark container as healthy or unhealthy. Restart na the work of restart policy, so restart: unless-stopped na wetin go bring container back after e exit, and e go also bring am back after host reboot as long as Docker service dey enabled. Confirm am with sudo systemctl is-enabled docker. To act specifically when container unhealthy, you need watcher outside Docker wey go read the status and restart the service.