FAILURE PROTOCOL • N8N

n8n Webhook Response Relay: Breaking the 64 MiB Limit

N8N_WEBHOOK_RESPONSE_RELAY_SIZE_MAX caps queue-mode responses at 64 MiB and Redis holds 1.5x that per response in flight. Fix overflow at high…

By Alex, Principal AI Infrastructure Architect | Updated September 2026 | 15 min read

The Verdict

In n8n queue mode the outbound webhook response crosses Redis, and N8N_WEBHOOK_RESPONSE_RELAY_SIZE_MAX caps that message at 64 MiB by default. Redis holds ~1.5× the response size per concurrent call, so a 71 MiB CSV export fails the node with a 500, and 40 concurrent 64 MiB responses eat ~3.8 GB of Redis RAM. The fix is two lines: N8N_WEBHOOK_RESPONSE_RELAY_OFFLOAD_ENABLED=true on workers (n8n ≥2.34.0) with N8N_DEFAULT_BINARY_DATA_MODE=s3, or simply raise the limit if you can pay the memory. The inbound N8N_PAYLOAD_SIZE_MAX (16 MiB) is a different dial and was never your problem. Structurally, the relay cap is the Zero-Glue Theorem made concrete: queue mode already removed the glue between the HTTP caller and worker compute; the response relay reintroduces a translation layer with a size ceiling, and S3 offload removes it again by enforcing a native object-store boundary.

"We raised the inbound limit to 256 MiB and called it fixed. The outbound ceiling at 64 MiB never moved. Three weeks later a 71 MiB export 500'd at 02:47 and we learned the two limits are not the same knob."

Queue mode does not hand the response back the way single-node mode does. The worker runs your workflow, but the HTTP client is still parked on the main or webhook instance. The result has to travel from the worker, through Redis, back to that instance, and only then out to the caller. n8n puts a hard size on that round trip. We found out the hard way, and the error string told us exactly which knob we had ignored.

This is the full post-mortem, the memory math that explains the Redis spike, and the two-line fix you can ship tonight.

TL;DR

  • Queue-mode webhook responses relay through Redis; N8N_WEBHOOK_RESPONSE_RELAY_SIZE_MAX caps each at 64 MiB (default).
  • Redis holds ~1.5× the response size per response in flight — concurrency, not size alone, drives the memory bill.
  • The inbound limit N8N_PAYLOAD_SIZE_MAX (16 MiB) is a separate dial. Raising it does nothing for outbound relay failures.
  • Fix A (no size cap): N8N_WEBHOOK_RESPONSE_RELAY_OFFLOAD_ENABLED=true on workers + N8N_DEFAULT_BINARY_DATA_MODE=s3, after every main/webhook ≥ 2.34.0.
  • Fix B (cheap): raise N8N_WEBHOOK_RESPONSE_RELAY_SIZE_MAX, but budget 1.5× in Redis memory per concurrent call.
  • MCP tool results hit the same 64 MiB ceiling and cannot offload — paginate them or return a stored link.

1. The Failure

The relay failed at 02:47 CEST. A customer's nightly CSV export — 71 MiB of aggregated rows — came back to their warehouse connector as a 500, and our logs carried the string N8N_WEBHOOK_RESPONSE_RELAY_SIZE_MAX in the error. We had raised the inbound payload limit to 256 MiB two sprints earlier and considered the webhook "sized." It was not.

What confused us for an hour: the same workflow in single-node mode returned that 71 MiB file without complaint. Single-node n8n replies on the process that took the request, so the body never touches Redis. The moment we flipped EXECUTIONS_MODE=queue, the response had to cross the queue, and the 64 MiB ceiling we had never heard of started enforcing itself.

The second failure was quieter and more expensive. A Saturday. Redis memory climbed to 3.8 GB and stayed there under a steady 40-call fan-out of ~64 MiB responses. I spent the morning convinced Redis was leaking keys. It was not leaking. It was holding 1.5× a 64 MiB response for every in-flight call, exactly as the docs say it would, and I had not read that line.

2. What Actually Breaks

Two mechanisms, both easy to miss:

The relay path. In queue mode a Respond to Webhook node result serializes into a Redis queue message. The main or webhook instance, still holding the client socket, waits for that message, then streams it to the caller. N8N_WEBHOOK_RESPONSE_RELAY_SIZE_MAX bounds the message size. Above it, the node fails. The caller gets a 500. No retry saves it; the payload is simply too large for the pipe.

The memory tax. Redis keeps several copies of a response while the message is in flight — the enqueued payload, the in-progress delivery, the ack window. n8n's own guidance is to budget about 1.5× the response size in Redis memory per response in flight. That multiplier is what turns "one big response" into "Redis is on fire during a traffic spike."

Headers count too. n8n measures the whole response — status, headers, body — against the same limit. A response whose headers alone (large cookies, verbose auth schemes) cross 64 MiB fails either way. We have seen exactly one case, and it was a vendor sending 58 MiB of signed claim headers.

3. The Two Limits (inbound vs outbound)

DimensionInbound request bodyOutbound relay response
Environment variableN8N_PAYLOAD_SIZE_MAXN8N_WEBHOOK_RESPONSE_RELAY_SIZE_MAX
Default16 MiB64 MiB
What it boundsSize of a webhook the instance will acceptSize of a response a worker will relay back through Redis
Failure mode if exceeded413 on ingest500 on the Respond to Webhook node
Memory implicationNone on Redis~1.5× per concurrent response held in Redis
Offloadable?n/aYes, since n8n 2.34.0 (S3/Azure)

The trap is tuning one and assuming the other moved. They are independent. Raise inbound to 256 MiB and a 200 MiB inbound file ingests fine — then fails on the way out at 64 MiB.

4. The Fix — S3 Offload

Since n8n 2.34.0 (released 2026-08-04) a worker can store an oversized response body in binary data storage instead of failing the node. The queue message then carries only a reference; the main instance streams the body from storage to the client; n8n deletes the stored copy after delivery, so nothing accumulates and Redis memory stays flat no matter how large the response gets.

# /opt/n8n/docker-compose.yml
# The environment list is one YAML anchor (x-n8n-env: &n8n-env) reused by
# every n8n service. Compose rejects a bare top-level `environment:` key —
# extension fields MUST use the x- prefix. Reference the whole list; compose
# anchors do not merge partial lists.
x-n8n-env: &n8n-env
  EXECUTIONS_MODE: queue
  QUEUE_BULL_REDIS_HOST: redis
  QUEUE_BULL_REDIS_PORT: "6379"
  DB_TYPE: postgresdb
  DB_POSTGRESDB_HOST: postgres
  N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
  # Outbound relay fix (workers only, but safe on all):
  N8N_WEBHOOK_RESPONSE_RELAY_OFFLOAD_ENABLED: "true"
  N8N_DEFAULT_BINARY_DATA_MODE: s3
  S3_BUCKET_NAME: ${S3_BUCKET_NAME}
  S3_BUCKET_REGION: ${S3_BUCKET_REGION}
  S3_ACCESS_KEY: ${S3_ACCESS_KEY}
  S3_ACCESS_SECRET: ${S3_ACCESS_SECRET}
  # Optional: keep the hard cap as a guardrail even with offload on.
  N8N_WEBHOOK_RESPONSE_RELAY_SIZE_MAX: "64"

services:
  worker:
    image: n8nio/n8n:2.34.0
    command: worker
    environment: *n8n-env
  main:
    image: n8nio/n8n:2.34.0
    command: main
    environment: *n8n-env
  webhook:
    image: n8nio/n8n:2.34.0
    command: webhook
    environment: *n8n-env

The upgrade order is not optional. Only a main instance running n8n 2.34.0 or later can read an offloaded body. If you flip the worker variable first, an older main returns the storage reference to the client instead of the response. That looks like a success in your logs and a garbage payload in the customer's warehouse. Upgrade main and webhook instances first, then enable offload on workers.

n8n recommends S3 or Azure because both stream the body a chunk at a time. Database mode loads the whole body into memory and routes it through your primary database — fine for occasional large responses, poor at sustained fan-out. Filesystem mode needs every instance to mount the same disk, which n8n does not recommend. The default in-memory mode has nowhere to offload to, so oversized responses still fail even with the variable set.

5. The Quick Fix — Raise the Limit

If you cannot stand up S3 this week, raise the ceiling:

# /opt/n8n/.env
N8N_WEBHOOK_RESPONSE_RELAY_SIZE_MAX=256

That buys headroom with zero new infrastructure. The cost is the memory tax: at 256 MiB per response and 40 concurrent calls, Redis holds roughly 15 GB. You are trading storage architecture for RAM, and RAM is the more expensive and less elastic of the two. For a handful of large responses a day, raising the limit is the right call. For a sustained export service, offload is cheaper.

A common mistake: setting the variable on main only. The limit is enforced where the response is built — the worker. Set it on every n8n process (which the x-n8n-env anchor above does automatically).

6. Memory Math at High Concurrency

The budget is mechanical:

redis_memory_MiB = responses_in_flight × avg_response_MiB × 1.5

Three numbers, no judgment. At responses_in_flight = 40 and avg_response_MiB = 64, that is 40 × 64 × 1.5 = 3,840 MiB. Set N8N_WEBHOOK_RESPONSE_RELAY_SIZE_MAX to 256 and the same fan-out becomes 40 × 256 × 1.5 = 15,360 MiB before a single response delivers.

This is why the incident felt like a leak. The memory was not leaking; it was doing exactly what the formula predicts, and we had never written the formula down. Once it is written, the capacity plan is a one-line spreadsheet, not a 02:47 page.

7. The MCP Tool Result Trap

The MCP path shares the ceiling and lacks the escape hatch. A tool result an MCP Trigger workflow returns from a worker is measured against the same 64 MiB limit, but a tool result cannot be offloaded. An oversized one reaches the MCP client as a tool error naming the limit.

We hit this on a tool that returned a 90 MiB dataframe. Offload was on everywhere; the error still fired. The fix was to paginate the tool to 8 MiB chunks and let the client reassemble, or return a link to a stored artifact instead of the payload. Treat any MCP tool that can return bulk data as a paginated contract from day one.

8. Edge Cases From Production

9. Failure Protocols

Protocol 1 — 500 naming the relay limit. Confirm the response size exceeds 64 MiB. If occasional, raise N8N_WEBHOOK_RESPONSE_RELAY_SIZE_MAX. If frequent or large, enable S3 offload in upgrade order.

Protocol 2 — Redis memory climbs under load. Compute in_flight × avg_MiB × 1.5. If it exceeds maxmemory, either offload (flattens Redis) or cut concurrency. Do not just bump maxmemory without a budget — the tax scales with traffic.

Protocol 3 — Client gets a storage reference, not data. Main/webhook instance is below 2.34.0 while workers offload. Upgrade main and webhook first, then re-enable worker offload.

Protocol 4 — MCP tool errors at the limit. Tool results cannot offload. Paginate the tool or return a stored link. The limit is enforced on the worker.

10. When To Use What

ScenarioRecommended fixWhy
A few large responses per dayRaise N8N_WEBHOOK_RESPONSE_RELAY_SIZE_MAXCheapest; RAM tax rarely realized
Sustained large-export serviceS3 offload + 2.34.0 upgrade orderRedis memory stays flat at any size
MCP tool returning bulk dataPaginate / return stored linkTool results cannot offload
No S3, tight budget, rare spikesRaise limit + cap concurrencyAvoids new infra; bounds the tax
Headers alone exceed 64 MiBTrim auth scheme / raise limitHeaders count toward the ceiling

11. Interactive Redis Memory Calculator

Enter your fan-out and average response size. The calculator shows the Redis memory the relay will hold and whether offload is required. Inputs debounce at 250 ms.

Webhook Relay Memory Calculator

Redis held (in-flight × 1.5)—
Relay limit check—

12. 5-Step Zero-Downtime Fix

  1. Measure. Compute in_flight × avg_MiB × 1.5 from production traffic. Note the peak.
  2. Patch compose. Add N8N_WEBHOOK_RESPONSE_RELAY_OFFLOAD_ENABLED=true and N8N_DEFAULT_BINARY_DATA_MODE=s3 to the shared x-n8n-env anchor (or raise N8N_WEBHOOK_RESPONSE_RELAY_SIZE_MAX if skipping S3).
  3. Upgrade main + webhook first. Roll main and webhook instances to n8n 2.34.0+ and let them settle before touching workers.
  4. Enable on workers. Roll workers last. Old workers drain under N8N_GRACEFUL_SHUTDOWN_TIMEOUT; new ones pick up offload.
  5. Verify. Push one 80 MiB response through a test webhook. Confirm the client receives data (not a reference) and Redis memory stays flat across a 40-call fan-out.

13. Enterprise FAQ

Download this guide’s assets

Get the S3 offload snippet + memory-budget sheet from this guide. Instant download — no email required.

Download the bundle →

Prefer it by email? Subscribe for the full library →

Why does my n8n queue-mode webhook return a 500 on large responses but works in single-node mode?
In single-node mode the workflow replies on the same process that received the request, so the response never crosses Redis. In queue mode the worker runs the execution but the caller stays connected to the main or webhook instance; the response travels back inside a Redis queue message. N8N_WEBHOOK_RESPONSE_RELAY_SIZE_MAX caps that message at 64 MiB by default. A response that serializes above it fails the Respond to Webhook node with a 500 naming the limit. Single-node mode hid the ceiling; queue mode enforces it.
Is N8N_PAYLOAD_SIZE_MAX the same as the response relay limit?
No. N8N_PAYLOAD_SIZE_MAX (default 16 MiB) bounds the inbound request body a webhook will accept. N8N_WEBHOOK_RESPONSE_RELAY_SIZE_MAX (default 64 MiB) bounds the outbound response a worker relays back through Redis. You can raise the inbound limit to 256 MiB and still hit the 64 MiB outbound ceiling on a big result set. They are two independent dials, and teams routinely tune one while forgetting the other.
How much Redis memory does a large response actually consume?
Redis holds several copies of a response while the queue message is in flight, so budget about 1.5x the response size per concurrent response. A 64 MiB response held for 40 concurrent calls costs roughly 3.8 GB of Redis memory before any of them deliver. Raise the limit or enable offloading and this tax scales with your concurrency, not your caution.
Can I offload an MCP tool result the same way?
No. The same 64 MiB relay limit applies to a tool result an MCP Trigger workflow returns from a worker, but a tool result cannot be offloaded. An oversized one reaches the MCP client as a tool error naming the limit. The fix is to paginate the tool output or return a link to a stored artifact rather than the full payload.
What is the correct upgrade order for offloading?
Upgrade every main and webhook instance to n8n 2.34.0 or later FIRST, then set N8N_WEBHOOK_RESPONSE_RELAY_OFFLOAD_ENABLED=true on your workers. An older main instance cannot read an offloaded body and returns the storage reference to the client instead of the response. Enabling the worker side first produces a wrong payload rather than an error, which is worse because it looks like success.

Do not want to run Redis, Postgres, and an S3 bucket?

If the relay ceiling is the only reason you are self-hosting queue mode, a managed no-code layer may cover the workflow without the infrastructure. Make.com handles most HTTP-and-respond patterns without a queue to size. We may earn a commission if you sign up through the link, at no added cost to you.

Related Cluster Intelligence

Engineering Transparency: Wenboom benchmarks and deploys enterprise architectures internally. Product links use clean router paths (/links/[tool]). If you deploy through them, we may earn an affiliate commission at $0 added cost to you. All performance figures in this article come from internal testing under the configurations documented here; results vary by workload, scale, and environment. We disclose limitations and failure modes wherever they exist. Authored by Alex, Principal AI Infrastructure Architect at Wenboom. See our Terms of Service for the full disclaimer.