Zelfgehoste n8n-automatisering voor links betekent drie dingen op servers die je zelf beheert: een n8n-instantie in Docker, een openbaar HTTPS-endpoint dat Elido-webhookevents ontvangt en uitgaande aanroepen naar de Elido-API met een token met beperkt bereik. Link- en domeinevents komen ondertekend binnen en events per klik staan op de roadmap. Je workflows bepalen wat er daarna gebeurt en de uitvoeringslogs verlaten je infrastructuur nooit.
Dat is de hele architectuur. De rest van dit artikel is het deel dat de quickstarts overslaan: hoe je webhooks door een reverse proxy krijgt, hoe je de handtekening controleert zodat een vreemde je workflows niet kan starten, wat de wachtrijmodus verandert en wanneer n8n Cloud eerlijk gezegd de betere keuze is. Ik draai deze opzet op één kleine VM en de bewegende delen passen op één scherm.
Wil je ook de links zelf op je eigen hardware, dan is dat een aparte en veel grotere klus. Het draaiboek voor Elido zelf hosten op k3s behandelt dat. Hier blijft Elido beheerd en verhuist alleen de automatiseringslaag naar je eigen huis.
Waarom n8n zelf hosten voor linkautomatisering
De gebruikelijke reden is data. Een workflow met een zelfgehoste n8n en een URL-verkorter ziet elke payload die hij verwerkt: de bestemmings-URL, tags, soms een campagnenaam die meer zegt dan je zou willen, en land, apparaat en referrer als je klik-analytics erin haalt. Bij n8n Cloud staan die uitvoeringen op andermans servers onder andermans standaard bewaartermijnen. Zelfgehost staan ze in je eigen Postgres, in de regio die jij koos.
Onder artikel 28 van de AVG heeft elke verwerker die persoonsgegevens aanraakt een contract nodig en een plek in je registers. Minder verwerkers, minder papierwerk. Moet je marketingdata al in de EU blijven, dan legt de gids voor dataresidentie in de EU uit waarom de automatiseringslaag net zo zwaar telt als de verkorter.
De tweede reden is de kostenvorm. n8n Cloud rekent per uitvoering en een drukke webhooktrigger of een frequente analytics-poll verbruikt uitvoeringen snel. Zelfgehost is een uitvoering een rij in een tabel en een paar milliseconden CPU.
De derde is bereik. Een zelfgehoste instantie staat op hetzelfde netwerk als je CRM-database of interne ticketingtool, dus een linkevent kan terechtkomen in een systeem dat nooit voor internet bedoeld was.
De Docker Compose-stack
Een minimale stack voor n8n-linkautomatisering met Docker heeft vier services nodig. De eigen Docker Compose-gids van n8n is de referentie; dit is de versie waar ik voor linkwerk mee zou beginnen, met de wachtrijmodus al aan zodat je later niet hoeft te migreren.
services:
postgres:
image: postgres:16
environment:
POSTGRES_USER: n8n
POSTGRES_PASSWORD: ${PG_PASSWORD}
POSTGRES_DB: n8n
volumes: [pgdata:/var/lib/postgresql/data]
redis:
image: redis:7
n8n:
image: docker.n8n.io/n8nio/n8n
env_file: .env.n8n
ports: ["127.0.0.1:5678:5678"]
depends_on: [postgres, redis]
n8n-worker:
image: docker.n8n.io/n8nio/n8n
command: worker
env_file: .env.n8n
depends_on: [n8n]
volumes:
pgdata:
En het gedeelde omgevingsbestand:
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=postgres
DB_POSTGRESDB_USER=n8n
DB_POSTGRESDB_PASSWORD=change-me
EXECUTIONS_MODE=queue
QUEUE_BULL_REDIS_HOST=redis
N8N_ENCRYPTION_KEY=generate-a-long-random-string
N8N_WEBHOOK_URL=https://n8n.example.com/
N8N_PROXY_HOPS=1
Twee regels tellen zwaarder dan ze lijken. De versleutelingssleutel moet op het hoofdproces en elke worker identiek zijn, anders kunnen workers de Elido-credential niet ontsleutelen en mislukt elke uitvoering met een verwarrende authenticatiefout. En poort 5678 is alleen aan localhost gebonden, omdat de reverse proxy het enige is dat naar internet mag kijken.
De Elido-API aanroepen vanuit zelfgehoste n8n
Uitgaande aanroepen hebben niets nodig buiten wat n8n meelevert. Maak een Header Auth-credential met de naam Authorization en als waarde Bearer gevolgd door een API-sleutel uit het Elido-dashboard en gebruik die vanuit de ingebouwde HTTP Request-node. n8n versleutelt hem in rust met de sleutel uit het omgevingsbestand, een reden te meer dat die sleutel overal moet overeenkomen.
Elke linkroute is gebonden aan een werkruimte. Om een URL te verkorten, doe je een POST naar https://api.elido.app/v1/workspaces/{workspace_id}/links met een JSON-body:
{
"domain_id": 12,
"destination_url": "{{ $json.url }}",
"title": "Spring launch",
"tags": ["n8n", "spring"]
}
Laat slug weg en Elido genereert er een. De domain_id is het korte domein waarop de link leeft; een GET naar /v1/workspaces/{workspace_id}/domains toont die van jou en ik zou de ID in de workflow vastzetten in plaats van hem bij elke run op te zoeken. Een GET op dezelfde linkroute toont links en met PATCH /v1/workspaces/{workspace_id}/links/{link_id} wijzig je achteraf een bestemming of tags.
Zet een Idempotency-Key-header op de POST, opgebouwd uit iets stabiels in het triggerende item, zoals een rij-ID. Probeert n8n de node na een timeout opnieuw, dan speelt de API het eerste antwoord opnieuw af in plaats van een tweede link aan te maken. De quickstart voor API en SDK's behandelt de rest van het oppervlak en de pagina over API en SDK's is er als je later liever een stap naar code verplaatst.
Er is ook een verpakte community node, n8n-nodes-elido, bedoeld om deze aanroepen in een vriendelijker vorm te verpakken. Hij staat op npm (versie 0.2.0) maar is nog optioneel, en omdat het geen geverifieerde node is, installeert hij alleen op zelfgehoste n8n, wat de opzet is die dit artikel toch al aanneemt. Houd de HTTP Request-versie als basis. Installeer je toch community-pakketten in wachtrijmodus, bedenk dan dat de GUI alleen in de hoofdcontainer installeert; workers zien hem nooit. Vanaf n8n 2.21 lost de route via installatie met omgevingsvariabelen dat op door bij het opstarten elke container af te stemmen, al verwijdert die bij de eerste keer alles wat niet op de lijst staat.
Elido-webhooks door je reverse proxy krijgen
Events stromen de andere kant op. Elido stuurt link.created, link.updated en domain.verified (onder andere) naar een endpoint dat je registreert onder Settings, Webhooks, en op zelfgehoste n8n is dat endpoint de productie-URL van een Webhook-node. Een event per klik, click.created, staat op de roadmap maar is nog niet opgeleverd, dus klikdata komt voorlopig uit een geplande pull van de analytics-API. De volledige eventlijst en payloadvormen staan in de referentie webhooks voor linkevents.
Achter een proxy bouwt n8n zijn webhook-URL op uit zijn eigen protocol, host en poort, wat betekent dat hij graag http://localhost:5678/webhook/... bekendmaakt aan wie erom vraagt. De pagina over de reverse-proxyconfiguratie is kort en het waard om helemaal te lezen: zet N8N_WEBHOOK_URL op je openbare adres, zet N8N_PROXY_HOPS=1 en laat de laatste proxy X-Forwarded-For, X-Forwarded-Host en X-Forwarded-Proto doorsturen. Oudere gidsen zeggen WEBHOOK_URL; huidige releases lezen die nog wel maar loggen een waarschuwing over verouderd gebruik.
Nginx, Traefik, wat je al draait is prima. Wat telt is TLS aan de openbare kant en dat het pad /webhook/* ongemoeid bij n8n aankomt.
Eén timingdetail kostte mij tijd. Elido wacht tien seconden op een antwoord voordat hij een bezorging als mislukt telt. Een workflow die naar een trage spreadsheet-API schrijft en dan antwoordt, kan daar doorheen schieten, opnieuw worden geprobeerd en dezelfde rij twee keer schrijven. Zet de Webhook-node op direct antwoorden en doe het werk daarna.
Bouw je dit voor een klant en wil je dat de webhookkant voor je wordt geregeld, dan toont de functiepagina over webhooks waarop je je kunt abonneren voordat je iets bouwt.
De handtekening controleren voordat er iets draait
Een openbare webhook-URL is een openbare URL. Wie hem vindt, kan een nep-event POSTen en je workflow starten, dus de eerste node na de trigger moet een handtekeningcontrole zijn.
Elke bezorging van Elido draagt X-Webhook-Signature (waarde v1= plus een hex-digest), X-Webhook-Timestamp in Unix-seconden, X-Webhook-Event en X-Webhook-Delivery. De digest is HMAC-SHA256 over de timestamp, een punt en de ruwe requestbody, met als sleutel het whsec_-geheim dat eenmalig wordt getoond wanneer je het endpoint aanmaakte.
Zet eerst Raw Body aan op de Webhook-node. Dat is de stap die mensen overslaan. Hash je JSON.stringify($json.body) in plaats van de exacte bytes die Elido stuurde, dan verschillen sleutelvolgorde of witruimte en mislukt elke handtekening, en je brengt een avond door in de overtuiging dat het geheim fout is. Dan een Code-node:
const crypto = require("crypto");
const item = $input.first();
const h = item.json.headers;
const raw = (await this.helpers.getBinaryDataBuffer(0, "data")).toString(
"utf8",
);
const ts = Number(h["x-webhook-timestamp"]);
if (Math.abs(Date.now() / 1000 - ts) > 300) throw new Error("stale delivery");
const expected =
"v1=" +
crypto
.createHmac("sha256", $env.ELIDO_WEBHOOK_SECRET)
.update(`${ts}.${raw}`)
.digest("hex");
const got = h["x-webhook-signature"] || "";
if (
got.length !== expected.length ||
!crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected))
) {
throw new Error("bad signature");
}
return [{ json: JSON.parse(raw) }];
De crypto-module is standaard beschikbaar in de Code-node, dus daar is geen extra configuratie nodig. Het venster van vijf minuten blokkeert het opnieuw afspelen van een onderschept verzoek. Wanneer je het geheim vervangt, stuurt Elido tijdens de overgangsperiode ook X-Elido-Signature-Previous, zodat je beide sleutels kunt accepteren terwijl je de variabele bijwerkt. De gids voor het controleren van webhookhandtekeningen heeft een versie van deze node die beide headers controleert, plus dezelfde controle in Node, Python en Go.
Wachtrijmodus en nieuwe pogingen
Met de handtekeningcontrole op zijn plek is de resterende vraag wat er gebeurt onder belasting of als iets uitvalt. Er spelen twee systemen voor nieuwe pogingen, en ze dekken verschillende storingen.
In de wachtrijmodus van n8n ontvangt het hoofdproces de webhook, maakt een uitvoering aan en geeft de ID aan Redis; workers pakken hem op. Een piek aan events hoopt zich op in de wachtrij in plaats van het HTTP-antwoord te laten haperen. Voor meer inkomend volume kun je aparte webhookprocessors achter een load balancer zetten, al is voor de meeste linkworkloads één hoofdproces en twee workers ruim voldoende.
De kant van Elido vangt op dat n8n onbereikbaar is. Elk niet-2xx-antwoord of elke timeout wordt na 1, 5 en 15 minuten opnieuw geprobeerd; na drie pogingen wordt de bezorging als mislukt gemarkeerd en kun je hem vanuit het dashboard opnieuw inplannen. Dat dekt een herstart van een container of een snelle deploy. Het dekt geen storing van een weekend, en daarom zou ik webhooks koppelen aan een nachtelijke afstemming die links via de API oplijst, zoals het artikel webhooks versus pollen bepleit.
Gebruik X-Webhook-Delivery als idempotentiesleutel. Nieuwe pogingen hergebruiken hem.
Zelfgehoste n8n versus n8n Cloud: eerlijke afwegingen
Ik geef de voorkeur aan zelf hosten hiervoor, maar ik heb teams er spijt van zien krijgen. Hier is de vergelijking zonder verkooppraatje van beide kanten.
| Aandachtspunt | Zelfgehoste n8n | n8n Cloud |
|---|---|---|
| Waar uitvoeringsdata staat | Je eigen servers, je eigen regio, je eigen bewaartermijn | De infrastructuur en standaarden van n8n |
| Interne systemen bereiken | Zelfde netwerk als je CRM en databases | Alleen wat je openbaar maakt |
| Openbare webhook-URL en TLS | Jij draait de proxy en de certificaten | Meegeleverd |
| Upgrades, back-ups, patchen | Jouw taak, elke maand | Geregeld |
| Kosten bij hoog eventvolume | Vaste serverkosten | Schaalt met uitvoeringen |
De laatste rij snijdt aan twee kanten. Een kleine VM is goedkoop, maar een uur van een engineer aan een mislukte upgrade niet, en n8n brengt vaak releases uit. Heeft niemand in het team de machine in beheer, dan is Cloud met de HTTP Request-node en de REST-API van Elido de verstandiger keuze, en de recepten voor Make en IFTTT of de Zapier-gids laten zien hoe dat beheerde pad op andere platformen eruitziet.
Wat ik je niet kan zeggen, is of je functionaris voor gegevensbescherming een zelfgehoste machine eenvoudiger zal vinden dan een verwerkersovereenkomst met een leverancier. Naar mijn ervaring wel, maar dat hangt af van hoe goed de machine wordt beheerd. Hoe het contract aan de kant van de verkorter werkt, vind je het best in de AVG-gids voor URL-verkorters. En ben je klaar om de eerste workflow aan te sluiten, pak dan een API-token in een werkruimte en laat een Webhook-node ernaar wijzen.
Verwant op de blog
- Elido zelf hosten op k3s: het draaiboek - als de links ook in eigen huis moeten leven.
- Webhooks voor linkevents - elk eventtype en elke payloadvorm.
- Webhooks versus pollen voor klikregistratie - waarom de nachtelijke afstemming ertoe doet.
- Automatisering van korte links met Make en IFTTT - de beheerde low-coderoute.
- n8n-URL-verkorter - de opzet van de HTTP Request-node en drie workflows in detail.
- Webhookhandtekeningen controleren - ruwe body, replayvenster en geheimrotatie in vier runtimes.
Veelgestelde vragen
Kan ik een URL-verkorter gebruiken met zelfgehoste n8n?
Ja. Zelfgehoste n8n kan elke verkorter met een REST-API aanroepen via de ingebouwde HTTP Request-node. Voor Elido betekent dat een Header Auth-credential met je API-sleutel en een POST naar de links-route van de werkruimte. Binnenkomende events zoals link.created komen binnen via de ingebouwde Webhook-node van n8n, die een openbare HTTPS-URL nodig heeft. Een event per klik, click.created, staat gepland maar is nog niet beschikbaar.
Hoe installeer ik community nodes op zelfgehoste n8n?
Gebruik op één instantie Settings, dan Community Nodes, en plak de npm-pakketnaam. In wachtrijmodus bereikt de GUI-installatie je workers niet, dus installeer het pakket in elke container of vermeld het vanaf n8n 2.21 in N8N_COMMUNITY_PACKAGES met N8N_COMMUNITY_PACKAGES_MANAGED_BY_ENV op true. Voor Elido heb je er geen nodig: de HTTP Request-node dekt de API.
Wat is WEBHOOK_URL in n8n?
Het vertelt n8n welk openbaar adres hij in de editor moet tonen en bij externe diensten moet registreren, omdat n8n dat achter een reverse proxy niet uit zijn eigen host en poort kan afleiden. Huidige n8n-versies lezen N8N_WEBHOOK_URL en loggen een waarschuwing over verouderd gebruik voor de oudere WEBHOOK_URL. Combineer het met N8N_PROXY_HOPS=1 en doorgestuurde headers op de proxy.
Hoe controleer ik een webhookhandtekening in n8n?
Zet de optie Raw Body in de Webhook-node aan en voeg dan een Code-node toe die HMAC-SHA256 opnieuw berekent over de timestamp-header, een punt en de ruwe body, met het geheim van je endpoint. Vergelijk het in constante tijd met de handtekeningheader en weiger alles wat ouder is dan vijf minuten. Controleer tegen de ruwe bytes, nooit tegen opnieuw geserialiseerde JSON.
Heeft de wachtrijmodus van n8n Redis nodig?
Ja. In wachtrijmodus zetten de hoofdinstantie en eventuele webhookprocessors binnenkomende triggers om in uitvoerings-ID's en duwen die naar een wachtrij op Redis, waaruit workers ze ophalen. n8n raadt wachtrijmodus met SQLite ook af, dus plan voor Postgres. Elk proces moet dezelfde versleutelingssleutel delen, anders kunnen workers opgeslagen credentials niet lezen.
Is zelfgehoste n8n beter voor de AVG dan n8n Cloud?
Dat kan, omdat jij kiest waar workflowdata, uitvoeringslogs en credentials fysiek staan en wie ze beheert. Het is niet automatisch conform: je wordt zelf verantwoordelijk voor patches, toegangsbeheer, back-ups en bewaartermijnen. Bij linkautomatisering die klikdata verwerkt, haalt zelfhosten in een EU-regio één verwerker uit je registers, wat de administratie eenvoudiger maakt.
Probeer Elido
Plak een URL, krijg een werkende korte link
Geen aanmelding nodig. Link blijft 30 dagen actief. Meld je aan om hem voor altijd te bewaren.
Geen account nodig. Meld je gratis aan voor 50 links per maand.