Hi everyone,
I’m running into a reliability issue with webhook-triggered workflows in n8n.
My setup looks like:Webhook → Process Data → HTTP Request → Database
The external service retries the webhook if it doesn’t get a fast enough response, which sometimes causes:
• Duplicate workflow executions
• Duplicate DB inserts
• Duplicate API calls/emails
The payload includes an event ID:{
“event_id”: “evt_12345”,
“user_id”: 42
}
Right now, if the same webhook is sent twice, both executions run fully.
I’m trying to figure out the cleanest production approach for:
• Deduplication
• Preventing concurrent processing of the same event
• Handling retries safely
I’ve considered:
• Storing processed event_ids in DB
• Using Redis locks
• Queue-based processing
• Returning webhook responses immediately and processing async
For people handling high-volume webhook systems in n8n:
• What pattern has worked best for preventing duplicate executions and side effects?
), please contact support at help@n8n.io →
What is the error message (if any)?
Please share your workflow
(Select the nodes on your canvas and use the keyboard shortcuts CMD+C/CTRL+C and CMD+V/CTRL+V to copy and paste the workflow.)
Share the output returned by the last node
Information on your n8n setup
- n8n version:
- Database (default: SQLite):
- n8n EXECUTIONS_PROCESS setting (default: own, main):
- Running n8n via (Docker, npm, n8n cloud, desktop app):
- Operating system:
Hi @Keira_Becky The most reliable approach is usually:Webhook → Save/check event_id → Process
The key idea is to treat event_id as a unique identifier.
Store the event_id in your database with a UNIQUE constraint before processing anything.
Example:INSERT INTO webhook_events (event_id)
VALUES ({{$json.event_id}})
ON CONFLICT DO NOTHING;
If insert succeeds → process the workflow
If it already exists → skip it
This help to Prevents duplicate processing
Handles webhook retries safely
Stops duplicate emails/API calls/DB inserts
You can also try to Return the webhook response quickly and process the heavy work asynchronously if possible. That reduces retries from the external service.
Welcome @Keira_Becky to our community! I’m Jay and I am a n8n verified creator.
The DB unique constraint approach Niffzy mentioned is solid. For a pure n8n approach without extra DB setup, you can use $getWorkflowStaticData('global') to track processed event IDs directly in the workflow - store the event_id in a Set node to the static data object, then check on each run before processing. This works for moderate volumes but doesn’t scale to high throughput.
For production, the cleanest combination is: return the webhook response immediately using Respond to Webhook node (set to “Respond First”), then continue the heavy processing after - this cuts most retries at the source. Layer on top a DB check with a UNIQUE constraint on event_id as Niffzy described, and you’ve covered both the retry-prevention and the actual deduplication. If you’re self-hosting with queue mode enabled, also set EXECUTIONS_TIMEOUT and concurrency limits to avoid pile-ups during spikes.
Great breakdown of the problem. Here’s the production approach I’ve used that covers all three of your concerns cleanly:
1. DB-based deduplication (your best bet for most setups)
At the very start of your workflow, before any processing, do a database lookup on event_id. If a row exists with status processed or processing, respond with 200 immediately and stop. If nothing found, insert a row with status processing — this acts as your lock.
The key is doing the insert with a UNIQUE constraint on event_id so concurrent executions race to insert and only one wins. The loser gets a constraint violation which you catch and exit cleanly.
2. Respond to the webhook immediately (Respond to Webhook node)
Use n8n’s “Respond to Webhook” node early in your workflow — before the heavy processing — to return 200 instantly. This stops the external service from timing out and retrying in the first place. Then continue processing in the same execution. This alone eliminates most duplicate scenarios.
3. Queue mode for true concurrency protection
If you’re self-hosting and need bulletproof deduplication under load, run n8n in queue mode (with Redis/Bull). Combined with the DB check above, you get serialized processing with no race conditions.
Practical recommendation:
- Respond to webhook early → kills most retries at the source
- DB dedup check with UNIQUE constraint → handles the rest
- Redis locks are overkill unless you’re processing thousands of events/min
The combination of fast response + idempotent DB writes is production-grade and doesn’t require Redis.