SSD Nodes Learn 🎉 VPS vanaf $5.50/mnd
Gidsen Matt ConnorDoor Matt Connor

n8n gaat steeds offline op VPS: oorzaken en oplossingen

Ervaart u dat n8n offline gaat op uw VPS? Wij leggen uit hoe u het verschil ziet tussen een OOM-kill, een defecte websocket, een restart-loop of een haperende workflow-trigger.

Waarom n8n offline gaat: vier fouten, één symptoom

"n8n gaat steeds offline" is één zin die vier verschillende fouten dekt, en voor elk daarvan is een andere oplossing nodig. De editor toont een melding dat de verbinding is verbroken, terwijl de container normaal draait. De container herstart uit zichzelf. De kernel beëindigt het Node.js-proces omdat het te veel geheugen verbruikt. Of er is helemaal niets mis met het proces, maar een actieve workflow wordt simpelweg nooit getriggerd. Wijzig de verkeerde instelling en u bent een heel weekend kwijt aan een probleem dat u nooit had.

Stel daarom vast met welke fout u te maken heeft voordat u de configuratie aanpast. n8n draait als één enkel Node.js-proces, meestal binnen één Docker-container, achter een reverse proxy die TLS (transport layer security) afhandelt. Elk van deze lagen kan op een eigen manier falen, en de browser rapporteert ze allemaal met dezelfde melding.

Diagnose in deze volgorde

Voer deze commando's uit op de VPS (virtual private server) en lees de waarden die uw eigen machine weergeeft. Vergelijk deze niet met getallen uit een forumdiscussie. De waarden die hier van belang zijn, beschrijven uw eigen server, niet die van iemand anders.

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

De kolom STATUS uit docker ps -a geeft aan hoe lang de container zich in de huidige status bevindt. Vergelijk dit met het moment waarop uw probleem begon. Als de container al lang voor het verschijnen van de melding actief was, is n8n nooit offline gegaan. Wat is verbroken, is de verbinding tussen uw browser en de backend; dit is het websocket-pad dat in de volgende sectie wordt behandeld.

RestartCount is het aantal keren dat Docker deze container heeft herstart. Noteer het getal, wacht een minuut en lees het opnieuw af. Een getal dat oploopt terwijl u kijkt, duidt op een restart loop. De logregels van vlak voor elke herstart bevatten de reden.

OOMKilled is een true of false vlag. True betekent dat de Linux-kernel het proces heeft beëindigd omdat het een geheugenlimiet heeft overschreden, hetzij de limiet van de container zelf, hetzij die van de gehele machine. Dit ene veld onderscheidt een memory kill van elk ander type exit; daarom controleert u dit voordat u aannames doet.

ExitCode is de code waarmee uw container voor het laatst is afgesloten. U hoeft niet uit uw hoofd te weten wat elke code betekent. Lees de uwe af en bekijk vervolgens het einde van docker logs met dezelfde tijdstempel. De log-tail en de out of memory-vlag vertellen samen wat er is gebeurd; beide afzonderlijk kunnen u op het verkeerde been zetten.

docker stats toont het actuele geheugengebruik naast de geldende limiet. Laat dit in een tweede terminal draaien, activeer de workflow die de fout veroorzaakt en observeer wat het getal doet terwijl de fout optreedt.

De banner voor een verloren verbinding wordt meestal veroorzaakt door uw reverse proxy

De n8n-editor houdt één langdurige push-verbinding open naar de backend om de voortgang van de uitvoering naar het canvas te streamen. Standaard is deze verbinding een WebSocket, wat wordt geselecteerd door N8N_PUSH_BACKEND, met als standaardwaarde websocket. Een WebSocket begint als een gewoon HTTP-verzoek met de headers Connection: Upgrade en Upgrade: websocket. De server antwoordt met 101 Switching Protocols, waarna beide partijen dezelfde TCP-socket in beide richtingen gebruiken.

Twee zaken kunnen dit verstoren, en beide bevinden zich in de proxy in plaats van in n8n. De proxy communiceert via HTTP/1.0 met de upstream of verwijdert de upgrade-headers, waardoor de upgrade nooit plaatsvindt en de editor continu probeert opnieuw verbinding te maken. Of de upgrade slaagt, maar de proxy sluit de socket later omdat deze inactief is; een WebSocket zonder berichten ziet er namelijk precies uit als een inactieve verbinding. In beide gevallen is de container in orde. De banner is de browser die aangeeft dat het kanaal is verbroken.

Bevestig dit in de browser voordat u wijzigingen aanbrengt. Open de ontwikkelaarstools, ga naar het tabblad Network, filter op WS en herlaad de editor. Het push-verzoek moet 101 Switching Protocols bereiken en open blijven staan. Een push-verzoek dat een normale statuscode retourneert, of een verzoek dat elke paar seconden opnieuw verschijnt, wijst op een probleem met de proxy.

De nginx-instellingen die de verbinding met de editor in stand houden

nginx stuurt een upgrade-verzoek niet door tenzij u daarom vraagt. proxy_pass communiceert standaard via HTTP/1.0 met de backend, en Connection en Upgrade zijn hop-by-hop headers die nginx tijdens het doorsturen verwijdert. U moet beide headers expliciet terugplaatsen. Het map-blok hoort in de http-context, niet binnen 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 is de regel die vaak wordt vergeten. De standaardwaarde is 60 seconden en deze is ook van toepassing op een geüpgradede WebSocket. Hierdoor verliest een editor-tabblad dat openstaat op een inactieve instantie de verbinding ongeveer een minuut na het laatste bericht. Door deze waarde te verhogen, lost u de melding op die verschijnt wanneer u terugkeert naar een tabblad dat u open had laten staan.

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

nginx -T toont de volledige actieve configuratie in plaats van slechts één bestand; hiermee controleert u of uw wijziging daadwerkelijk is geladen. Een configuratie die in een bestand staat dat door geen enkele include-regel wordt ingeladen, is de reden waarom een correcte oplossing geen effect lijkt te hebben.

Geef vervolgens aan n8n door dat het zich achter een proxy bevindt, omdat de applicatie URL's opbouwt op basis van deze waarden.

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 staat standaard op 0, wat betekent dat n8n het verbindende adres als het clientadres beschouwt en X-Forwarded-For negeert. Stel dit in op het aantal proxy's dat zich voor de container bevindt. Sinds augustus 2026 is N8N_WEBHOOK_URL de huidige naam; de oudere variabele WEBHOOK_URL werkt nog steeds, maar geeft bij het opstarten een waarschuwing over veroudering.

Traefik stuurt WebSockets door, maar verbreekt de verbinding na een time-out

Traefik stuurt een WebSocket-upgrade door zonder middleware of extra labels. Een Traefik-gebruiker die deze melding ziet, heeft daarom meestal te maken met een time-out in plaats van een ontbrekende header. De instellingen hiervoor bevinden zich op het entryPoint. Sinds augustus 2026 in Traefik v3 staat idleTimeout standaard op 180 seconden en readTimeout standaard op 60 seconden.

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

Caddy handelt de upgrade automatisch af in reverse_proxy en vereist hiervoor geen specifieke instructie. Als u de proxy niet kunt wijzigen omdat deze door een andere partij wordt beheerd, schakel dan over naar het push-kanaal met N8N_PUSH_BACKEND=sse. SSE (server-sent events) is een normale HTTP-respons die open wordt gehouden; dit overleeft een proxy die upgrades weigert, al kan een agressieve idle-time-out de verbinding alsnog verbreken. De keuze voor de proxy zelf is een afzonderlijke beslissing, en de vergelijking tussen nginx, Caddy en Traefik beschrijft wat het beheer van elk van deze opties van u vraagt.

Wanneer de container daadwerkelijk blijft herstarten

Als RestartCount oploopt, faalt de container en start Docker deze telkens opnieuw op. Leg de tijdstempels in de logboeken naast de herstartmomenten en lees wat er direct daaraan voorafging. Vier oorzaken dekken bijna alle gevallen: een configuratiefout die de opstart blokkeert, een database die n8n niet kan bereiken, een crash tijdens het draaien en een beëindiging door geheugengebrek (OOM kill).

Begin bij de volume-mount, aangezien rechtenproblemen vaak onopgemerkt blijven. De officiële image draait als de niet-geprivilegieerde gebruiker node en slaat data op in /home/node/.n8n. Een bind mount die door root is aangemaakt, is niet beschrijfbaar voor deze gebruiker, waardoor het proces bij elke opstart direct sterft en het restart-beleid dit achter een lus verbergt.

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

Een named volume voorkomt dit probleem volledig, omdat Docker deze aanmaakt met de juiste eigendomsrechten. Als u een bind mount moet gebruiken, voer dan chown uit op de host-directory naar de numerieke user ID die het eerste commando toonde. Het begrijpen van de eigendomsrechten tussen host en container is eenmalig essentieel, en de PUID en PGID uitleg behandelt hoe deze images bepalen wie de bestanden mag schrijven.

De out of memory-kill die op een crash lijkt

Er zijn twee afzonderlijke geheugenlimieten boven een n8n-proces, en ze falen op verschillende manieren. De control group-limiet van de container wordt afgedwongen door de kernel: zodra deze wordt overschreden, wordt het proces onmiddellijk beëindigd, zonder de mogelijkheid om nog iets weg te schrijven, en is OOMKilled waar. De V8 heap-limiet wordt afgedwongen binnen Node.js: zodra deze wordt overschreden, genereert Node een heap-fout met een stack trace en sluit het proces zichzelf af, waardoor OOMKilled onwaar is. Vanuit de browser zien deze er identiek uit. Vanuit docker inspect verschillen ze slechts één veld.

Stel de Node heap-limiet in onder de containerlimiet. Als de heap-limiet de hoogste van de twee is, blijft V8 geheugen toewijzen tot voorbij het punt waarop de kernel ingrijpt. Hierdoor bereikt de garbage collector nooit zijn eigen limiet en krijgt u altijd de hardere foutmelding zonder logbestand om te lezen.

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>

Kies beide getallen op basis van wat uw VPS daadwerkelijk heeft, waarbij u ruimte overlaat voor de database, de proxy en het besturingssysteem. docker stats --no-stream toont het huidige verbruik naast de geldende limiet, zodat u kunt controleren of de limiet die u heeft ingevoerd de limiet is die Docker heeft toegepast. Hoe Compose-geheugenlimieten worden toegepast gaat dieper in op welke instelling voorrang krijgt wanneer er meerdere zijn opgegeven.

Uitvoeringsgegevens groeien onder uw beheer

Eén uitvoering bevat de output van elk knooppunt terwijl de run actief is, en n8n slaat die gegevens vervolgens op. Dit heeft twee gevolgen. Het piekgeheugen van een run wordt bepaald door de grootste batch gegevens die u erdoorheen stuurt; een workflow die tienduizend rijen tegelijk verwerkt, is dus een ander programma dan dezelfde workflow die er tweehonderd per keer verwerkt. Bovendien blijft de opgeslagen kopie groeien totdat deze wordt verwijderd.

Pruning lost het tweede probleem op. Sinds augustus 2026 zijn de standaardinstellingen: pruning ingeschakeld, EXECUTIONS_DATA_MAX_AGE op 336 uur (14 dagen) en EXECUTIONS_DATA_PRUNE_MAX_COUNT op 10000. Dit is ruim bemeten voor een kleine VPS die op SQLite draait, waarbij één bestand alles bevat en hetzelfde proces dat de editor bedient, dit bestand ook moet lezen en schrijven.

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 is de agressieve instelling. Deze bewaart mislukte uitvoeringen voor debugging en verwijdert succesvolle uitvoeringen. Kies hier bewust voor, want een workflow die foutieve output produceert zonder een foutmelding te geven, laat u anders niets na om te inspecteren. Pruning markeert rijen eerst als verwijderd en verwijdert ze tijdens een latere ronde. Omdat SQLite vrijgekomen pagina's hergebruikt in plaats van ze terug te geven, krimpt het bestand op de schijf niet direct op het moment dat u de instelling wijzigt.

Om de piekbelasting te verlagen in plaats van het totale opgeslagen volume, moet u per run minder gegevens verwerken. Splits grote taken op in sub-workflows die kleine resultaten terugsturen naar de parent, gebruik batching met het Loop Over Items knooppunt en houd volledige datasets buiten het Code knooppunt.

Binaire bestanden horen niet in het geheugen thuis

N8N_DEFAULT_BINARY_DATA_MODE staat standaard ingesteld op default, waardoor binaire data in het geheugen van het actieve proces blijft staan. Elk bestand dat een node downloadt, en elke kopie die aan de volgende node wordt doorgegeven, blijft daar staan totdat de uitvoering is voltooid. Eén workflow die enkele grote bijlagen ophaalt, kan het proces voorbij een limiet duwen die bij normale JSON-verwerking nooit wordt bereikt. Daarom treedt de crash op bij een specifieke workflow en niet op een vast tijdstip.

environment:
  - N8N_DEFAULT_BINARY_DATA_MODE=filesystem

Met filesystem wordt binaire data geschreven onder N8N_BINARY_DATA_STORAGE_PATH, wat standaard in de n8n-gebruikersmap staat en dus op hetzelfde volume terechtkomt als de rest. Controleer of het volume voldoende ruimte heeft voordat u overschakelt. N8N_PAYLOAD_SIZE_MAX bepaalt de maximale omvang van een inkomende webhook-payload in MiB (mebibytes) en staat standaard op 16. Door dit te verhogen, staat u grotere verzoeken toe; dit is een geheugenbelasting die u bewust accepteert.

Alles wat de server verder deelt, concurreert om hetzelfde RAM-geheugen. Als de OOM-kills zijn begonnen nadat u een databasecontainer hebt toegevoegd, is het draaien van de database in Docker of op de host de afweging die u nu maakt.

Restart-beleid en herstel na een reboot

Een container zonder restart-beleid blijft gestopt na het afsluiten en na een reboot van de host. restart: unless-stopped zorgt ervoor dat de container in beide gevallen opnieuw opstart, terwijl een handmatig gestopte container gerespecteerd blijft. restart: always start ook een container opnieuw die u bewust heeft gestopt, zodra Docker weer opstart.

n8n biedt een health-endpoint, gedefinieerd door N8N_ENDPOINT_HEALTH, dat standaard healthz is. Controleer dit eerst vanaf de host zodat u zeker weet dat het pad correct is op uw instantie.

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

Een healthcheck op zichzelf start niets opnieuw op. Compose markeert de container als ongezond en stopt daar, dus de healthcheck heeft een restart-beleid of een externe watcher nodig om effect te sorteren. Een healthcheck schrijven die daadwerkelijk actie onderneemt en de stack laten herstarten na een reboot behandelen beide onderdelen.

De workflow die niet wordt geactiveerd terwijl n8n correct functioneert

In dit scenario verschijnt er geen banner en vindt er geen herstart plaats. De container is actief, de editor werkt, maar de verwachte uitvoering ontbreekt in de lijst met executies. Vier oorzaken verklaren het merendeel van deze gevallen.

  • De workflow is niet actief. Een Schedule Trigger werkt alleen via het productiepad; het testen in de canvas-omgeving plant dus niets in.
  • De tijdzone is niet de uwe. GENERIC_TIMEZONE gebruikt standaard America/New_York, waardoor een schema dat is ingesteld op 09:00 uur ook daadwerkelijk om 09:00 uur in die tijdzone wordt uitgevoerd, totdat u GENERIC_TIMEZONE en TZ aanpast aan uw eigen instellingen.
  • Downtime wordt niet achteraf ingehaald. Triggers worden geregistreerd wanneer n8n opstart; een schema dat gepland stond terwijl de container herstartte, wordt dus niet alsnog uitgevoerd. De eerstvolgende uitvoering vindt plaats op het eerstvolgende geplande tijdstip na het opstarten.
  • De workflow is voor u gedeactiveerd. N8N_WORKFLOW_AUTODEACTIVATION_ENABLED staat standaard uit. Wanneer deze optie is ingeschakeld, wordt een workflow die herhaaldelijk crasht automatisch offline gehaald, waarna deze er precies zo uitziet als een workflow die nooit is geactiveerd.

Open de lijst met executies en filter op de betreffende workflow. Een vermelding die is mislukt, duidt op een probleem in de workflow zelf. Als er helemaal geen vermelding is, betreft het een probleem met de trigger en zijn de vier bovenstaande oorzaken de juiste plekken om te zoeken.

Wat u als eerste moet wijzigen

  1. Lees STATUS, RestartCount en OOMKilled op uw eigen container door voordat u een bestand bewerkt.
  2. Als de container niet is uitgevallen, corrigeer dan de proxy-upgrade-headers en de idle-timeout.
  3. Als OOMKilled op true staat, stel dan een containerlimiet in die u bewust heeft gekozen, plaats het Node heap-plafond daaronder en schakel binaire data over naar filesystem.
  4. Als er niets is geactiveerd, controleer dan of de workflow actief is en of de tijdzone van de instantie overeenkomt met die van u.

Het meeste hiervan is configuratie die u eenmalig instelt en daarna kunt vergeten, bovenop een werkende installatie. Als u die installatie nog aan het opbouwen bent, is de handleiding voor n8n op Docker met HTTPS de basis waar deze instellingen bij horen.

FAQ

Waarom toont de n8n-editor een melding dat de verbinding is verbroken terwijl de container draait?

De editor houdt een WebSocket open om de voortgang van de uitvoering te streamen. Als uw reverse proxy de headers Connection: Upgrade en Upgrade: websocket niet doorstuurt, of geen HTTP/1.1 upstream gebruikt, wordt de upgrade nooit voltooid. De browser blijft dan continu opnieuw verbinding maken, terwijl n8n zelf correct functioneert. In nginx heeft u proxy_http_version 1.1 nodig, plus beide proxy_set_header-regels, en een proxy_read_timeout die langer is dan de standaard 60 seconden, zodat een inactief tabblad niet wordt afgesloten. Controleer de actieve configuratie met sudo nginx -T, niet het bestand dat u heeft bewerkt.

Hoe onderscheid ik een 'out of memory'-kill van een gewone crash?

Voer docker inspect n8n | grep -iE 'OOMKilled|ExitCode|RestartCount' uit en lees de vlag OOMKilled. 'True' betekent dat de kernel het proces heeft beëindigd omdat het een geheugenlimiet overschreed; in de containerlogboeken staat hierover niets nuttigs omdat het proces geen kans kreeg om te schrijven. 'False', in combinatie met een heap-fout en een stack-trace aan het einde van docker logs, betekent dat Node.js zijn eigen V8-heaplimiet heeft bereikt en zelf is afgesloten. Stel NODE_OPTIONS=--max-old-space-size in op een waarde onder uw containerlimiet, zodat u de tweede foutmelding krijgt; deze laat namelijk wel sporen na.

Maakt het opschonen van uitvoeringsgegevens direct schijfruimte vrij?

Nee. EXECUTIONS_DATA_PRUNE markeert oude uitvoeringen voor verwijdering en een latere taak verwijdert deze, volgens het schema dat is ingesteld met EXECUTIONS_DATA_PRUNE_HARD_DELETE_INTERVAL. Bij SQLite hergebruikt het bestand bovendien vrijgekomen pagina's in plaats van deze terug te geven aan het bestandssysteem, waardoor de grootte op schijf enige tijd gelijk blijft nadat de rijen zijn verwijderd. Stel EXECUTIONS_DATA_MAX_AGE en EXECUTIONS_DATA_PRUNE_MAX_COUNT in op waarden die bij uw systeem passen en controleer de volgende dag opnieuw in plaats van direct.

Waarom is mijn geplande workflow niet uitgevoerd terwijl n8n herstartte?

n8n registreert triggers wanneer het proces start en voert geen schema's uit die tijdens de downtime hadden moeten plaatsvinden. Een herstartlus resulteert daarom in stilte in plaats van een reeks inhaalacties; de volgende uitvoering vindt plaats op het eerstvolgende geplande tijdstip na de opstart. Als u uitvoeringen nodig heeft die niet gemist mogen worden, stuur de workflow dan aan via een externe aanroeper die een webhook raakt, zodat de retry-logica buiten n8n ligt.

Zal een healthcheck n8n herstarten wanneer het niet meer reageert?

Niet uit zichzelf. Een Compose-healthcheck markeert de container alleen als 'healthy' of 'unhealthy'. Herstarten is de taak van het restart-beleid; restart: unless-stopped zorgt ervoor dat de container terugkeert nadat deze is afgesloten, en ook na een herstart van de host, mits de Docker-service is ingeschakeld. Bevestig dit met sudo systemctl is-enabled docker. Om specifiek op een 'unhealthy'-status te reageren, heeft u een externe watcher buiten Docker nodig die de status leest en de service herstart.