Slow n8n workflow? How to diagnose and optimize n8n performance
Published 31 July 2026 · 6 min read
A workflow that took ten seconds in testing takes twenty minutes in production, the editor lags, executions pile up: "n8n is slow" is one of the most frequent — and vaguest — diagnoses among self-hosted users. In almost every case, the slowness is neither an n8n bug nor an undersized server, but a workflow pattern that needlessly multiplies operations, or an instance setting left on its default value. This guide walks through the method in order: measure first, fix the classic causes next, tune the instance last.
Measure before optimizing: per-node duration
The fundamental reflex: never optimize on intuition. Open a finished execution from the executions list — n8n shows, for each node, how long it took and how many items it processed. This execution detail is the same tool you use to debug a workflow, read here through the lens of time rather than data.
What you will almost always find: one or two nodes account for 80 to 95% of the total duration. Optimizing the others is wasted effort. And the stakes are not cosmetic: as soon as a human is waiting for the result — a webhook behind a form, a chatbot, a Slack approval — latency has a measured cost. Jake Brutlag's study at Google, "Speed Matters for Google Web Search" (2009, see on Google Scholar), showed through a controlled experiment that an artificial delay of just 400 milliseconds reduced the number of searches per user by 0.6% — and that the effect persisted even after the delay was removed. A few hundred milliseconds change user behavior; several seconds make them give up.
The five-step diagnostic plan
- Reproduce on a realistic volume: a workflow that is fast on 10 test items can be slow on production's 5,000 items, and the cause lies precisely in that ratio.
- Open the execution detail and note, node by node, the duration and the item count.
- Identify the dominant node — the one that concentrates most of the time.
- Check whether its duration grows with the item count. A duration proportional to volume points to per-item processing (one network call or query per item); a fixed but high duration points instead to a single call that is too heavy (unfiltered query, large file).
- Apply the matching remedy (following sections), then re-measure on the same volume. Without a before/after measurement, there is no way to know whether you gained anything.
Cause #1: processing item by item what could be done in bulk
The most expensive pattern: a loop that runs one network operation per item. One hundred items = one hundred SQL queries, one hundred API calls, one hundred round trips each paying the full network latency.
The canonical example is the SQL query loop. Instead of hitting the database once per customer:
-- Inside the loop, executed 500 times:
SELECT * FROM orders WHERE customer_id = '{{ $json.customer_id }}';
lift the logic into a single query with a join or an IN clause, executed once:
SELECT c.email, o.total, o.created_at
FROM customers c
JOIN orders o ON o.customer_id = c.id
WHERE o.created_at > NOW() - INTERVAL '1 day';
The Postgres node runs this query in one round trip, and the workflow drops from minutes to seconds. The same reasoning applies to app nodes: many offer operations that accept several items in one pass (bulk create, multi-upsert) — check the available operations before wrapping the node in a loop. The Loop Over Items pattern remains the right tool when the API on the other side offers no bulk operation, or to bound memory — but it is too often the default choice.
Cause #2: sequential API calls and inefficient pagination
The HTTP Request node processes its items one after another: 300 items at 500 ms of latency each is an incompressible 2 minutes 30. Three remedies, in order of preference: look for a bulk endpoint on the API side (many accept an array of objects in one POST); reduce the number of items upstream (filter before calling); or parallelize in small groups inside a Code node with Promise.all, while respecting the provider's quotas — aggressive parallelization trades a slowness problem for 429 rate-limit errors.
Pagination deserves the same scrutiny: fetching 10,000 records in pages of 20 generates 500 requests where pages of 200 need 50. Raise the page size to the maximum the API accepts and stop the loop as soon as the useful window is covered — our HTTP Request pagination guide details the three common mechanisms and their pitfalls.
Cause #3: Code node over thousands of items and binary data in memory
A Code node in "Run Once for All Items" mode transforming 10,000 items in one pass is fast. The real costs hide elsewhere: the "Run Once for Each Item" mode on large volumes, nested loops that search an array for every item (build a Map once instead), and copying large structures on every iteration.
Binary data is the other dead weight: a workflow that carries PDFs or videos from node to node keeps everything in RAM by default, which slows the execution long before the memory crash. Extract the useful metadata early, drop the binary as soon as it no longer serves, and switch the instance to filesystem mode — the full topic is covered in our guide to large files and binary data.
Cause #4: the monolithic workflow
An 80-node workflow that loads everything, transforms everything and writes everything in a single execution accumulates each step's data in memory and becomes impossible to measure precisely. Splitting into sub-workflows — a parent that orchestrates, children that each process a bounded batch — frees memory between calls and yields per-chunk durations directly readable in the history.
The instance settings that change the game
When the workflow itself is clean but the whole instance drags, look at the configuration:
# Don't save data for successful executions (errors are still kept)
EXECUTIONS_DATA_SAVE_ON_SUCCESS=none
EXECUTIONS_DATA_SAVE_ON_ERROR=all
# Automatic history pruning (default: 336 h, i.e. 14 days)
EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_MAX_AGE=168
By default, n8n records the full data of every successful execution: on a workflow running every minute with sizeable payloads, the database swells and everything slows down — the executions list, workflow saves, startup. Three complementary levers:
- SQLite vs PostgreSQL: SQLite (the default) is fine for testing, not for a production instance writing constantly. Moving to PostgreSQL (
DB_TYPE=postgresdb) is the first move on any serious instance; - Queue mode when the load is structural: if the problem is not one slow workflow but an overall volume saturating a single instance, queue mode with Redis and workers spreads the executions — without, remember, speeding up a poorly designed individual execution;
- Basic monitoring (CPU, memory, database size, execution durations) to spot drifts before they become outages.
Narrow the data window: fetch only the delta
Many slow workflows redo work every night that was already done: re-downloading the whole CRM, re-processing every file, re-comparing every record. Store the timestamp of the last run (in a Data Table, a file or the database) and request only what has changed since — filter[updated_at], a since parameter, a date-bounded SQL query. Incremental processing turns a one-hour batch into a thirty-second execution, and cuts the load on the called APIs accordingly.
Timeouts and retries: the slowness coming from the other side
Finally, a "slow" workflow is sometimes just waiting on an API that isn't answering: without an explicit timeout, an HTTP Request node can hang for long minutes on a silent server, and badly tuned retries multiply the waits. Set a timeout suited to each external call and a retry policy with backoff that fails fast and cleanly — the precise settings are covered in our HTTP Request retry and timeout guide.
Key takeaways
A slow n8n workflow gets fixed in order: measure per-node duration in the execution detail to find the dominant node; replace item-by-item processing with bulk operations (SQL with joins, batch endpoints, large pagination); lighten what travels through the workflow (binary data, incremental data windows); split monoliths into sub-workflows; and only then touch the instance — execution saving set to none, pruning, PostgreSQL, and queue mode when the overall load justifies it. Measure before, measure after: that is the only difference between optimizing and tinkering.
FAQ
Frequently asked questions
How do I find out which node is slowing down my n8n workflow?
Open a finished execution from the executions list: n8n shows, for each node, how long it took and how many items it processed. In the vast majority of cases, one or two nodes account for most of the total duration — that is where to act, not on the rest of the workflow.
Why does my n8n instance get slower over time, even without changing the workflows?
The usual suspect is execution history: by default, n8n saves the full data of every successful execution. On an instance that has been running for months, the database swells and everything slows down — especially on SQLite. Set EXECUTIONS_DATA_SAVE_ON_SUCCESS=none (or check pruning with EXECUTIONS_DATA_MAX_AGE), and move to PostgreSQL beyond light usage.
Should I switch to queue mode to fix a slow n8n workflow?
Not as a first move. Queue mode raises the instance's overall throughput (several executions running in parallel across workers), but it does not speed up an individual execution: a workflow looping over 5,000 SQL queries will be just as slow on a worker. Optimize the workflow itself first; queue mode is justified when the overall load is structurally too high for a single instance.
Is a Code node slower than a native node in n8n?
Not inherently: a Code node transforming a few thousand items in one pass is very fast. What gets expensive is the "Run Once for Each Item" mode on large volumes (the code is re-evaluated per item), network calls made inside a loop within the code, and copying large data structures on every iteration.
Bundle FlowKit Complet
€269