← Blog

Automation

n8n-audit checklist: wat we scoren vóór een retrofit

Drieënveertig workflows, 1,4 miljoen execution-rijen, de default encryption key staat nog in de .env. De audit die we draaien op elke n8n onder €15M voor we iets nieuws offreren.

Jacob Molkenboer· Oprichter · A Brand New Company· 18 jun 2026· 8 min
Klembord met messing rand, crème checklist, groen tabje, loep, potloodstompje en rode lakzegel op ivoorkleurig bureau.

Het is dinsdag in Eindhoven. De operations lead van een B2B-distributeur van €9M overhandigt ons SSH-toegang tot een t3.medium waarop n8n draait achter Caddy. Drieënveertig actieve workflows, het merendeel geschreven door een ontwikkelaar die vorig voorjaar vertrok. De N8N_ENCRYPTION_KEY in de .env is dezelfde die drie jaar geleden in de quick-start-gids stond. De execution_entity-tabel bevat 1,4 miljoen rijen. Ze willen er een agent bovenop die concepten schrijft voor binnenkomende offerteaanvragen. Voor we dat werk offreren, auditen we wat er al staat.

We hebben deze audit de afgelopen achttien maanden op meer dan zestig Nederlandse mkb-n8n-instances gedraaid. Het patroon is consistent genoeg om er een checklist van te maken. De score is er niet om de beheerder een cijfer te geven — het is een voorspelling van wat breekt zodra we er een agent bovenop leggen.

De encryption-key-score

N8N_ENCRYPTION_KEY versleutelt de data-kolom in de credentials_entity-tabel. Lekt er een Postgres-dump en staat die key ernaast in source control, dan ligt elk Exact Online-token, elke Moneybird API-key en elk Microsoft Graph refresh token in je workflows in platte tekst.

We scoren vier toestanden. Nul punten als de key dezelfde is die n8n bij eerste boot genereerde en nooit is gewijzigd. Eén punt als hij ooit één keer is geroteerd. Twee punten als hij in de laatste twaalf maanden is geroteerd. Drie punten als hij geroteerd is én in een secrets manager hangt — Doppler, 1Password, Bitwarden Secrets Manager, AWS Secrets Manager, maakt niet uit welke.

n8n heeft geen ingebouwd rotatiecommando. De procedure is mechanisch: exporteer ontsleuteld, wijzig de key, importeer opnieuw.

# 1. Export every credential, decrypted, with the current key
docker exec -it n8n n8n export:credentials --all --decrypted \
  --output=/tmp/creds.json

# 2. Stop n8n, replace N8N_ENCRYPTION_KEY in the env, start again
#    The DB now holds credentials the new key cannot read

# 3. Re-import — n8n encrypts with the new key on write
docker exec -it n8n n8n import:credentials --input=/tmp/creds.json

# 4. Shred the plaintext before anyone notices it existed
shred -u /tmp/creds.json

We draaien dit vanuit een one-shot container, niet vanaf de host, en we routeren de export via een tmpfs-mount zodat de platte tekst nooit een echte schijf raakt. Op een instance met veertig credentials kost de hele rotatie minder dan twee minuten downtime als je 'm scriptet.

Execution-history tegen de AVG-grens van dertig dagen

De AVG noemt geen dertig dagen als harde bovengrens voor n8n execution-logs. Artikel 5(1)(e) eist wél dat persoonsgegevens niet langer worden bewaard dan nodig. Voor binnenkomende webhook-payloads van een Shopify-shop, een Mollie-checkout of een Formidable-formulier hanteren we dertig dagen als default, tenzij de klant schriftelijk een langere bewaartermijn kan onderbouwen. De meesten kunnen dat niet.

Het default-gedrag van n8n is het probleem. Oudere zelf-gehoste versies bewaarden executions voor altijd. Recente versies prunen na 336 uur, maar alleen als je de flag zet. En zelfs met pruning aan: de historische rijen die al in execution_entity stonden toen je hem aanzette, blijven staan waar ze staan.

De config die wij uitleveren ziet er zo uit:

EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_MAX_AGE=720          # hours, = 30 days
EXECUTIONS_DATA_PRUNE_MAX_COUNT=50000
EXECUTIONS_DATA_HARD_DELETE_BUFFER=24
N8N_DEFAULT_BINARY_DATA_MODE=s3

De regel over binary data is belangrijker dan mensen denken. Default schrijft n8n webhook-bijlagen — facturen als PDF, KvK-uittreksels, scans van ID-bewijzen — naar /home/node/.n8n/binaryData op het container-filesystem. Die directory valt buiten execution pruning. Hij groeit door tot de schijf vol is, en in een back-up belandt hij in cold storage met een bewaartermijn die niemand heeft opgeschreven. Verplaats hem naar S3 met een lifecycle-rule, of naar een Hetzner Object Storage-bucket met dezelfde regel.

Voor de historische backlog is een one-shot SQL-prune sneller dan wachten tot de pruner is bijgekomen:

DELETE FROM execution_entity
WHERE "stoppedAt" < NOW() - INTERVAL '30 days';

DELETE FROM execution_data
WHERE "executionId" NOT IN (SELECT id FROM execution_entity);

VACUUM FULL execution_entity;

Die tweede statement telt: n8n splitst execution-metadata en payload over twee tabellen, en de foreign key heeft geen cascade.

De drie workflows die een queue-mode-cutover overleven

Queue mode is de voorwaarde voor alles wat we erbovenop willen bouwen. Het main-proces accepteert webhooks en duwt jobs via Bull naar Redis; één of meer worker-containers pakken ze op en draaien ze. Zonder queue mode kun je geen agent schalen die twintig parallelle LLM-calls fan-out — het single main-proces blokkeert elke andere webhook tot hij klaar is. De officiële walkthrough staat in de n8n queue-mode-docs, en de bijbehorende env-vars staan in de configuratie-referentie.

De cutover is waar in-flight verkooporder-webhooks verloren gaan. De default Bull-retrypolicy zet een mislukte job tot driemaal opnieuw in de queue. Als je downstream ERP niet idempotent is, maak je drie verkooporders in Exact voor één Shopify-event. De fix is een idempotency key in je workflow inbouwen voor je EXECUTIONS_MODE omzet.

Waarschuwing

Als een worker midden in een executie crasht, wordt de job vanaf het begin opnieuw in de queue gezet — niet hervat. Alles met niet-idempotente side effects tussen de trigger en het eerste await-punt vuurt twee keer. Audit die workflows vooraf, niet achteraf.

Van een typische instance met veertig workflows zijn de drie patronen die de cutover zonder herwerk overleven voorspelbaar:

  1. Stateless inbound webhook → externe API → 200. Een Shopify orders/create-webhook die de payload mapt naar een Exact Online verkooporder, post, en een 200 teruggeeft met het nieuwe order-ID. Geen Wait node, geen lokaal filesystem, geen instance-state.
  2. Cron-getriggerde DB-naar-DB-transformaties. Elke vijftien minuten nieuwe rijen lezen uit een Postgres-bron, transformeren, wegschrijven naar een Postgres-doel. De worker heeft geen state die de volgende worker niet óók heeft.
  3. Handmatig getriggerde async backfills. De 'run once'-workflows die de operations lead vanuit de UI start om een dag aan orders opnieuw te verwerken. Niks downstream wacht op het HTTP-antwoord, dus een trage handoff blijft onzichtbaar.

Dat zijn de canary's. Wij migreren die eerst, kijken tweeënzeventig uur de kat uit de boom, en zetten daarna de volgende batch over in groepjes van vijf. De workflows die eerst herwerk nodig hebben zijn net zo voorspelbaar: alles met een Wait node in resume-on-webhook-modus, alles wat het lokale filesystem leest of schrijft buiten binary data mode om, alles dat $env gebruikt voor runtime-config die de workers niet hebben, en alles dat synchroon op een webhook reageert met een body die meer dan tien seconden kost om uit te rekenen.

De volledige sheet

De encryption key, de retentie-instelling en de queue-mode-readiness zijn drie van de achttien items op de sheet. De overige vijftien, in de volgorde waarin we ze scoren:

  • Frequentie en bewaartermijn van de Postgres-back-ups, plus de datum van de laatste restore-test
  • SSO of 2FA op de n8n UI — LDAP, SAML, of minimaal afgedwongen TOTP
  • Reverse-proxy-TLS: auto-renewal, moderne ciphers, HSTS
  • Egress-allowlist op de n8n-host zodat een lekkende workflow nergens kan POSTen
  • Hardcoded secrets in workflow-JSON — we greppen de export op sk_live_, Bearer , en gangbare patronen
  • Webhook-authenticatie: header-check, HMAC-signature, of niets
  • Workflow-versionering via Git sync of een gedocumenteerde handmatige export-cadans
  • Error workflow geconfigureerd en gerouteerd naar een menselijk kanaal
  • Node- en n8n-versies gepind, niet :latest
  • Huidige n8n-versie afgezet tegen de gepubliceerde security advisories
  • Container-CPU- en -memory-limits ingesteld
  • Log-shipping naar een plek die een container-restart overleeft
  • PII-inventaris: welke workflows raken ooit een BSN, IBAN of volledige naam
  • Verwerkersovereenkomst met elke externe verwerker die de workflows aanroepen
  • Een geschreven DR-runbook dat iemand anders dan de oorspronkelijke auteur kan volgen

Elk item scoort 0–3. Het maximum is 54. Alles onder de 30 betekent: eerst een hardening-sprint offreren, dan pas agent-werk. Boven de 40 gaan we rechtstreeks door naar de retrofit. Tussen 30 en 40 onderhandelen we welke items in de project-scope vallen en welke op de backlog blijven.

Wat je vandaag kunt doen

Open je n8n-instance, draai n8n export:workflow --all --output=audit.json, en grep het bestand op sk_live_, Bearer en elke string die op een API-token lijkt. Een klusje van vijf minuten dat consistent tussen de nul en vier hardcoded credentials boven water haalt, en dat je vertelt of je op de encryption-key-vraag start met een 0 of een 3.

Toen we vorig voorjaar de order-router bouwden voor een B2B-distributeur in Brabant, brak de queue-mode-cutover precies één workflow die we niet hadden geflagd: een Wait node midden in de flow die hervatte op een callback van een vervoerder, en die afhing van een stukje instance-geheugen dat de handoff naar een worker niet overleefde. We betrapten 'm in het canary-venster (daarom is er een canary-venster), en het volledige audit-script zit in onze procesautomatisering-playbook.

Kern

Voor je agents bovenop n8n bouwt, audit drie dingen: de encryption key, execution-retentie tegen de AVG, en welke webhooks een queue-mode-cutover overleven.

FAQ

Wat is queue mode in n8n en waarom is het belangrijk voor agent-werk?

Queue mode scheidt het main-proces dat webhooks ontvangt van de worker-processen die ze uitvoeren, met Redis (Bull) als job queue. Het is de voorwaarde voor horizontaal schalen en voor elke agent die parallelle LLM-calls fan-out.

Hoe vaak moet ik de N8N_ENCRYPTION_KEY roteren?

Minimaal nadat een teamlid met toegang vertrekt. We geven de volle score voor rotatie in de afgelopen twaalf maanden, gecombineerd met opslag in een secrets manager in plaats van een platte .env.

Vereist de AVG een bewaartermijn van 30 dagen voor execution-logs?

Nee, de AVG noemt geen getal. Artikel 5(1)(e) eist dataminimalisatie, dus de retentie moet doelgebonden worden onderbouwd. Dertig dagen is de default die wij hanteren, tenzij de klant een langere termijn schriftelijk vastlegt.

Welke workflows breken bij de overstap van n8n naar queue mode?

Workflows met Wait nodes in resume-on-webhook-modus, alles dat naar het lokale filesystem schrijft buiten binary data mode om, alles dat $env leest voor runtime-config, en elke webhook die meer dan tien seconden nodig heeft om synchroon te antwoorden.

automationprocess automationworkflowsecurityoperationsarchitecture

Iets bouwen?

Start een project