← BenchTruth

Self-hosted n8n under sustained load: 2,880 events at a free-tier VM

A 4h42m soak on a 1 GB Google Cloud e2-micro, measured end to end · run 2026-09-05 · no affiliate links on this page

Nothing was lost. Latency improved. The worst delay of the entire run was caused by the machine's own package manager, and it was invisible to CPU and memory monitoring.

We had published a 10-event burst result, and a reader replied that a burst and hours of elevated load are different animals. That is correct, and it is the better question. So on September 5 2026 we fired one event every 5 seconds for 4 hours and 42 minutes at the self-hosted n8n instance that runs our own benchmark harness: 2,880 events, 136x its normal hourly rate, held continuously, while sampling host telemetry every 15 minutes.

Read the scale honestly before the results. 0.17 events per second is a long duration at a modest rate. This is a test of whether a small box degrades, leaks or drops work over hours, and the answer is no. It is not a throughput benchmark, and it says nothing about 50 events per second. The workflow was our reference WF1: one webhook trigger, one outbound HTTP POST. A heavy multi-step workflow on the same box would be a different measurement.

Result

Events fired2,880
Accepted by n8n2,876
Refused at send (loud failure)4
Accepted then lost (silent failure)0
Silent failure rate0 / 2,876

The four refusals are not counted in the failure rate, and that exclusion is deliberate. A refused send is a loud failure: our client got an error at the moment of sending, so it knows immediately and can retry. A silent failure is the opposite, an event that was accepted and then never arrived, with nothing anywhere reporting a problem. Mixing the two produces a number that means nothing. The reasoning is on silent failure vs rejected webhook.

Under load it got faster

The result we did not expect. Same workflow, same box, same receiver, compared against normal operation over the preceding 7 days:

medianp95p99
Normal operation (7 days, n=260)782 ms10,577 ms16,937 ms
Normal operation (30 days, n=1,120)822 ms10,322 ms25,517 ms
Under soak (n=2,876)610 ms1,673 ms5,392 ms

The tail improved by a factor of 6.3 while the load went up by a factor of 136, and the effect holds against 7, 14 and 30 day baselines, so it is not an artefact of stale comparison data.

The likely explanation, stated as an explanation and not a measurement: at roughly 4.5 events per hour this instance is idle almost all the time, and each arriving request pays a cold-path cost. Under a steady 5-second cadence nothing gets a chance to go cold. If that is right, then for low-traffic self-hosted automation the tail you see in production is mostly an idleness cost, not a capacity cost. We have not run the experiment that would confirm it (fire one event after a long idle period, compare), so we are not claiming it as a finding. What we do claim is the comparison in the table.

The worst delay of the run was the machine cleaning its own house

Every 30 minutes of the soak, bucketed. Note the 06:30 row:

bucket (UTC)firedrefusedundeliveredp50p95worst
04:0025300521 ms1728 ms32,831 ms
04:3031900581 ms796 ms4,032 ms
05:0030840608 ms782 ms13,100 ms
05:3031900579 ms783 ms2,445 ms
06:0031400590 ms2174 ms5,617 ms
06:3027300715 ms4727 ms56,726 ms
07:0031200658 ms1595 ms4,778 ms
07:3031700612 ms818 ms2,985 ms
08:0028900680 ms2363 ms36,194 ms
08:3017600536 ms965 ms7,669 ms

56.7 seconds, 93x the median for that bucket. Our first hypothesis was an n8n container restart, because container memory dropped from 252.5 MiB to 88.3 MiB in one sample, below its value at the start of the run. That hypothesis was wrong. The container reports restarts=0, started 2026-07-12, up 7 weeks, no OOM kill, and the host has been up 54 days. The memory reading fell because the kernel reclaimed page cache, which the container memory figure includes.

What actually happened is in the system journal. At 06:04:38 the machine's own apt-news.service started and activated the PackageKit daemon, which spent about 45 minutes refreshing package metadata before quitting at 06:49:49.

time (UTC)%user CPU%iowait%idledirty pages (kB)
06:101.652.6695.05204
06:201.462.0395.93388
06:304.0920.2071.5441,316
06:404.0434.2457.02524
06:501.6629.1268.12336
07:001.613.6493.88664

Disk I/O wait went from 2.7 percent to 34.2 percent for about 45 minutes on a shared-core VM with a slow persistent disk, and the request tail went with it. The load average of 2.72 we sampled at 06:36 fits: Linux counts processes blocked in uninterruptible disk wait toward load average, which is why a machine can show 4 percent user CPU and a load of 2.72 at the same time.

On a small free-tier VM, the operating system's own unattended housekeeping is a bigger perturbation than 136x your production traffic. Our load did not produce a single lost event in five hours. Its package manager produced a 56-second request.

And that incident was invisible to CPU and memory monitoring

This is the part worth carrying away. During those 45 minutes:

A monitoring setup watching CPU and memory would have reported a healthy machine for the entire window in which its p99 was 56 seconds. That is the same shape as the failure class this whole site exists to measure: the instrument was working correctly and reporting on the wrong layer. See uptime vs failure rate for the vendor-facing version of the same problem.

Four refusals we cannot explain, and a blind spot of our own

Four sends were refused, at 05:23:43, 05:23:58, 05:24:41 and 05:24:56 UTC. Sequence numbers 825, 826, 829 and 830 failed while 827 and 828 succeeded between them, so this was flapping, not a clean outage window.

We checked four sources for that moment and all four are quiet:

We do not know why those four were refused. Worse, we made it harder on ourselves: our harness recorded that each send failed but not why, because the error was caught and discarded. Connection reset, timeout and a non-2xx response are indistinguishable in our own ledger for these four events. We have fixed the instrument so the next run records the reason. A harness that measures other people's silent failures should not have a blind spot of its own, and reporting the gap is cheaper than pretending we know.

Method

Limits of this result

Questions

Can a 1 GB free-tier VM run self-hosted n8n?

For a low-volume webhook workload, our measurement says yes with room to spare. We hold a production n8n on a Google Cloud e2-micro (1 GB RAM, 0.25 vCPU baseline, no cost) and it has served our benchmark harness since July 2026. On September 5 2026 we fired 2,880 events at it over 4 hours 42 minutes, one every 5 seconds, which is 136x its normal hourly rate held continuously. It lost nothing: 2,876 of 2,880 sends were accepted and all 2,876 were delivered end to end. Note the honest limit of this result: 0.17 events per second is a long duration at a modest rate, not high throughput. It tells you a small box does not degrade or leak under hours of steady work. It does not tell you what happens at 50 events per second, and it was measured on a single-webhook-to-single-HTTP-call workflow, not a heavy multi-step one.

Does self-hosted n8n get slower under sustained load?

Ours got faster, which surprised us. Across the soak, median latency was 610 ms and the 95th percentile 1,673 ms. Over the same workflow in normal operation during the preceding 7 days, the median was 782 ms and the 95th percentile 10,577 ms. The tail improved by a factor of 6.3 under load. The most likely explanation is that low-traffic self-hosted instances pay a cold-path cost: at roughly 4.5 events per hour the instance is idle almost all the time, and each arriving request wakes something up. Under a steady 5-second cadence nothing gets a chance to go cold. We are labelling that an explanation, not a measurement, because we have not yet run the experiment that would confirm it: fire a single event after a long idle period and compare.

Why is my self-hosted n8n occasionally very slow for no reason?

Check disk I/O wait before you blame n8n or your workflow. The single worst latency in our entire 2,880-event run was 56.7 seconds, and it was not caused by our load. At 06:04 UTC the machine's own apt-news job woke the PackageKit daemon, which spent about 45 minutes refreshing package metadata. Disk I/O wait went from 2.7 percent to 34.2 percent and dirty pages spiked to 41,316 before returning to a few hundred. During that window CPU user time barely moved, from 1.6 percent to 4 percent, and free memory actually increased as the kernel reclaimed page cache. A CPU and memory dashboard would have shown a healthy machine. On a small shared-core VM, the operating system's own unattended housekeeping is a bigger perturbation than 136x your production traffic.

What is the difference between a burst test and a soak test?

A burst test fires a short spike and asks whether the system survives it. A soak test holds a steady rate for hours and asks whether the system degrades, leaks memory, or starts dropping work over time. They fail differently, and passing one says little about the other. We had previously published a 10-event burst result, and a reader pointed out, correctly, that a burst and hours of elevated load are different animals. This page is the soak test that comment asked for.