Spurlock Studios
Contact
Share LinkedIn X
Amber node beads on a dark rail. Thesis: RUN MULTIPLE SUB WORKFLOWS SIMULTANEOUSLY.

You run multiple sub-workflows simultaneously in n8n by putting reusable work in a child graph, calling it with the Execute Sub-workflow node (still labeled Execute Workflow on older canvases), setting Mode to Run once for each item, and turning Wait for Sub-Workflow Completion off when the parent does not need return data before it continues. That is the how-to. Worker --concurrency is not the how-to.

The Production n8n handbook is the spine. The concurrency trap — production caps skip child executions, and queue-mode children usually stay on the parent worker — lives in Why doesn’t worker concurrency cap my n8n sub-workflows. This spoke is the graph: Execute node, wait vs fire-and-forget, item fan-out, Split In Batches vs overlap, and rate limits.

Across 600+ automations built and 500+ live, the graphs that overlap children on purpose also name a batch ceiling the same day. Unbounded per-item calls are not “parallelism.” They are a stampede with extra execution rows.

The short answer

  • Build a child with an Execute Sub-workflow Trigger (“When Executed by Another Workflow”). Define inputs. Restrict This workflow can be called by.
  • Call it from the parent with Execute Sub-workflow. Source is usually Database / From list. Fill the child’s input fields. Do not leave the child on Accept all data if it writes.
  • Overlap = per-item Mode + Wait off. Wait on when the parent needs the child’s last-node output. Wait off when the parent only needs to launch work.
  • Bound overlap with Loop Over Items (Split In Batches) plus a Wait between slices. The loop is sequential. That is the point.
  • Pace against the vendor page, not the worker flag. n8n’s own rate-limit guidance is Loop Over Items + Wait, or HTTP Request Batching. --concurrency does not see these children.
GoalGraphNot the graph
Reuse a mapper, one result setOne child, Run once with all items, Wait onPer-item fan-out
Overlap independent itemsPer-item Mode, Wait off, named max itemsUnbounded Sheet → child
Pace a tight APILoop Over Items → one child per slice → WaitPer-item with Wait off and no ceiling
Spread work across workersChild starts on a published webhookExecute Sub-workflow in queue mode

Simultaneous is a Mode and a Wait toggle. Write both in the parent description so the next editor does not “fix” them.

How do you run multiple sub-workflows simultaneously in n8n?

You split the repeated graph into a child, then start one child execution per independent item, and you do not hold the parent on each child unless you need the return payload. Official docs name the node Execute Sub-workflow. Older JSON still says Execute Workflow. Same node type: n8n-nodes-base.executeWorkflow.

n8n’s looping page is the other half of the sentence: most nodes already process a list of items inside one execution. That is not multiple sub-workflows. Multiple sub-workflows means multiple child execution IDs under one parent.

Procedure to ship the first overlapping pair:

  1. Create the child. Add Execute Sub-workflow Trigger. Set Input data mode to Define using fields below (or a JSON example). Save.
  2. In child Settings, set This workflow can be called by to the parent you are about to wire — not “anyone.”
  3. In child Settings, attach the shared error workflow. Fire-and-forget children fail after the parent is gone. See error workflows operators actually read.
  4. In the parent, add Execute Sub-workflow. Source: Database, From list. Map the child’s fields.
  5. Set Mode to Run once for each item.
  6. Open Options. Turn Wait for Sub-Workflow Completion off if you want overlap. Leave it on if the next parent node needs child output.
  7. Put Loop Over Items in front of that node with a batch size you can defend. Name max items per parent run.
  8. Publish both. Fire the parent from a production webhook or schedule. Do not treat an editor Execute click as the proof.
PieceHealthy defaultFailure if you skip it
Child triggerWhen Executed by Another WorkflowParent has nothing to call
Input contractNamed fields / JSON exampleAccept all data into a write
CallersOne named parentTwo parents, one unbounded child
ModePer-item only after the list is boundedOne child per Sheet row, forever
WaitOn for return data; off for launchGreen parent, children still writing
ProofPublished parent, overlapping child timestampsCanvas click, zero child rows

n8n will refuse to trigger a child that currently has errors. Fix the child before you debug the parent.

A list processed inside one child is still one sub-workflow. Simultaneous means more than one child running at the same time.

What does the Execute Workflow node actually start?

It starts a different workflow on the same host, with its own execution row, linked both ways: parent node → View sub-execution, child execution → parent. It does not start a second n8n instance. It does not, on typical queue-mode builds, put a second job on Redis. That last fact is the trap spoke. Here: the node is a function call with an execution ID.

Official node parameters:

ParameterWhat you setWhat we actually use
SourceDatabase, Local File, Parameter (JSON), URLDatabase / From list for production
Workflow InputsFields pulled from the child’s triggerMapped, typed, no surprise nulls into writes
ModeAll items, or each itemAll items for reduce/map; each item for independent units
Wait for Sub-Workflow CompletionOn = parent pauses; off = parent continuesOn when you need the last node of the child back

Data path, from the same docs: Execute Sub-workflow in Workflow A passes items into the child’s trigger. The last node of Workflow B sends data back to that node in A — if the parent is still waiting. Fire-and-forget does not give you that return on the parent branch.

Decision list for Source:

  1. Production parent/child in the same instance → Database, pick from the list. IDs drift less than pasted JSON.
  2. Local File / Parameter / URL → throwaway or codegen. Not the production caller.
  3. You can create the child from the parent node (Create a sub-workflow) or extract selected nodes via Sub-workflow conversion. Same trigger still has to exist.
  • Child trigger is Execute Sub-workflow Trigger, not a leftover Manual Trigger
  • Input data mode is fields or JSON example, not Accept all data on a writer
  • Attempt to convert types is on only if you know the coercion
  • Parent maps every required field; removed fields arrive as null
  • You opened View sub-execution on a real run once

The child’s Input data mode is the contract. Pick it before you map the parent.

Child input modeParent seesUse when
Define using fields belowNamed, typed inputs on Execute Sub-workflowProduction writes. Default.
Define using JSON exampleShape inferred from the example objectYou already have a payload fixture
Accept all dataNo required fields; child must tolerate messPrototypes only. Never on a writer

If you remove a requested input on the parent, the child gets null for that field. Attempt to convert types will coerce when it can; do not rely on it to turn a string "false" into a safe skip.

Local File is not a concurrency strategy. It is a way to load JSON. Keep production children in the database.

Wait for completion vs fire-and-forget — which do you pick?

Wait on when the parent needs the child’s output, needs the child error to fail the parent, or must not start the next step until that unit is done. Wait off when the parent only launches work and each child owns its own finish, alert, and writes.

Official option text: Wait for Sub-Workflow Completion lets the main workflow wait before the next step (on) or continue without waiting (off). That is the parallel switch. It is not a rate limiter. It is not a queue.

WaitParentChild executionsUse when
On (typical default)Holds until that child returnsYou get last-node data backEnrichment, validation, anything the next node reads
Off (fire-and-forget)Continues; may finish while children runStill start; no return on this branchNotify, generate drafts, slow syncs the parent will not join

Hedge, because child graphs are not always a single straight line: if the child contains a long Wait, a webhook resume, or a loop that returns on a branch early, the parent can see “complete” while work is still open. Community reports of Wait-on parents advancing before an inner Loop Over Items finished are consistent with that. Force one exit: Merge (wait mode) or a single last node after the loop.

Procedure to pick the toggle without a debate:

  1. Does a downstream parent node read child fields? Wait on.
  2. If the child fails, must the parent fail? Wait on, and do not Continue on Fail on the Execute node.
  3. Is the child a background job the parent will not join? Wait off, error workflow on the child, idempotency in the child.
  4. If you cannot answer 1–3, leave Wait on. Launch-and-hope is how a green parent hides a 429.
Parent WaitChild later failsWho sees it
OnChild error can fail the parentParent error workflow, if attached and the run is automatic
OffParent already succeededChild error workflow only
On + Continue on FailChild error swallowedOften nobody

Continue on Fail on Execute Sub-workflow is a mute on the split. Fail closed on writes.

If Wait is off and you still need a later join, do not pretend the Execute node will return data. Collect out of band:

  1. Child writes a status row (Airtable / Postgres / Data Table) keyed by the business event before it finishes.
  2. Or child POSTs to a Wait resume URL / webhook the parent already armed — only if you designed that resume in the same execution that minted $execution.resumeUrl.
  3. Or you do not join. The child files needs_review and a human is the join. That is the content repurposing shape.

Fire-and-forget without a status row is how you ask Slack “did it finish?” at 5pm.

Wait off is launch. It is not “queue these children across workers.”

Run once for each item vs run once with all items

Mode is the multiplier. Wait is whether those executions overlap from the parent’s point of view.

Official wording: Run once with all items passes the whole list into one child execution. Run once for each item executes once per input item in turn. “In turn” is the docs. Overlap is what you observe when Wait is off and many child rows start together. Confirm on your version with timestamps, not with a YouTube clip.

ModeChild executions per parent runHonest use
Run once with all itemsOneMapping, reduce, shared lookup, one report
Run once for each itemOne per itemIndependent records, after you cap how many items exist

n8n’s looping docs also list Execute Workflow in Run Once for All Items as a node that does not auto-iterate. That is the same idea: all-items Mode is one call. Per-item Mode is N calls.

Worked shapes, not a benchmark:

Parent listModeWaitWhat you should see
20 rowsAll itemsOn1 child, parent holds
20 rowsEach itemOn20 children; parent waits on the node (serialize unless your build overlaps anyway — measure)
20 rowsEach itemOff20 children overlapping unless something else paces them
20 rows, Loop batch 5, one child per loop, Wait between loopsAll items on the sliceOn4 children, paced

Checklist before you ship per-item Mode:

  • Max items is named (query limit, pagination, hard slice)
  • Child is built for one item if you send one item
  • Wait matches whether you need return data
  • Writes in the child claim an idempotency key before the side effect
  • You counted child rows on one production parent; they match the list (or the loop), not a surprise 2×

If you cannot name max items, you do not have a Mode preference. You have a stampede setting.

Split In Batches vs parallel — what is each for?

Loop Over Items is the current UI name. The node type is still n8n-nodes-base.splitInBatches. Older exports say Split In Batches. Same pacer.

It saves the incoming list and, each iteration, returns a Batch Size slice on the loop output. When it finishes, it combines processed data on the done output. That is sequential slicing inside one execution. n8n documents it for processing all items and for avoiding rate limits. It is not a fan-out across workers. It is not --concurrency.

Default n8n behavior is the opposite instinct: many nodes already run once per item inside one execution (loop). You add Loop Over Items when you need a pause between slices, when a node is on the exception list (RSS Read, some inserts, HTTP pagination), or when you must not start the next N HTTP calls yet.

MechanismWhat runs at onceWhat it is for
Normal item processing (no loop node)One execution, node walks the listDefault. Not multiple sub-workflows
Loop Over Items, Batch Size NOne slice, then the nextPace. Rate limits. Pagination
Execute Sub-workflow, all itemsOne child executionReuse a graph on the whole list
Execute Sub-workflow, per item, Wait offMany child executions, overlappingActual simultaneous sub-workflows
Loop + per-item Execute inside the loopStill ~list-length children if you per-item the sliceEasy to rebuild the stampede with extra nodes

Procedure to convert a per-item bomb into paced overlap (or into serial, if the vendor is tight):

  1. Read the child’s contract. One item in, or a list in?
  2. Add Loop Over Items in the parent. Start Batch Size smaller than you want. You are buying a bound.
  3. Inside the loop: Execute Sub-workflow Run once with all items for that slice, or per-item on a small slice if the child must stay one-item-in.
  4. After the child (or after the HTTP inside the child), add Wait. Connect back to the loop.
  5. Count child rows on the next production parent. They should match loop iterations (or iterations × slice size), not the raw list.
If you wantPut this in the parent
True overlap, boundedLoop 5 → per-item Execute, Wait off, Wait between loops
Serial safetyLoop 1 → one Execute, Wait on, Wait after
One child, internal HTTP pacingAll-items Execute; HTTP Batching or Wait inside the child
Cross-worker spreadDo not use this node for that. Webhook children. Next section.

Two expressions from the Loop Over Items docs worth pinning on the If that guards pagination:

ExpressionReturnsUse
{{$("Loop Over Items").context["noItemsLeft"]}}booleantrue when the node has no remaining slice
{{$("Loop Over Items").context["currentRunIndex"]}}numberWhich iteration you are on (log it)

Reset on Loop Over Items re-initializes the list each iteration. That is for unknown-length pagination with an If exit. If the exit never matches, the execution loops until timeout. Include a termination condition.

Also do not confuse Execute Once (node Settings, process only the first item) with per-item Execute Sub-workflow. Execute Once on the parent HTTP node sends one request. Per-item Execute starts N children. Opposite knobs.

Split In Batches is how you refuse to start all the children at once. Parallel is the Execute Mode plus Wait off. Do not mash the two names into one Slack thread.

How do you fan items out without melting the vendor?

You name the unit of work, you cap how many child executions exist, you pace the slices, and you put irreversible writes behind a key in the child. Fan-out is a content-repurposing shape as much as a CRM shape: one approved extract → one child per surface. That pipeline already exists as content repurposing pipelines. The fan-out here is the Execute node. The human sign-off there still applies.

Decision list — copy into the parent description:

  1. Helper, no HTTP. One child, all items. Stop. You do not have a simultaneity problem.
  2. Independent reads. Per-item + Wait off, Loop batch you measured, Wait between batches.
  3. Independent writes (CRM, mail, charge). Same as 2, plus idempotency in the child, error workflow on the child, fail closed.
  4. Surfaces from one source (newsletter draft, LinkedIn draft, talk track). One child per surface, Wait off if filing is independent; Wait on if the parent must notify only after every surface exists.
  5. Need the worker pool. Stop using Execute Sub-workflow as the start path. Published webhook on the child.
Fan-outChild countBound
One extract → 3 surface children3Surface list is finite
Sheet of N leads → per-lead enrichNPagination / hard slice / Loop
Nested parent → child → grandchild via ExecuteTreeDepth 1 on the hot path
Two published parents, one childSum of both listsCallers setting

Procedure we use when a parent already fans in production:

  1. Freeze new per-item Execute calls.
  2. Count last night’s parent: child rows, overlap window, whether the child writes.
  3. Insert Loop Over Items. Batch size starts small.
  4. Move writes behind a key in the child.
  5. Attach error workflows on parent and child. Prove one automatic fail — editor Execute does not fire Error Trigger.
  6. Only then consider webhook children if you still need distribution.
  • Surface list or record list has a named ceiling
  • Child writes are keyed
  • Wait off only where the parent will not join
  • Nested Execute depth on the hot path is 1
  • Callers on the child are listed

A three-surface content fan-out is simultaneous work. A two-thousand-row Sheet with per-item Execute is a lockout. Same node. Different bound.

Where do rate limits actually live?

They live on the vendor, as requests per window, sometimes per resource, sometimes per token. n8n does not inherit Airtable’s 5/sec because you set worker concurrency to 5. n8n hits an API, gets 429, and shows “The service is receiving too many requests from you” (handle rate limits, HTTP Request 429).

Sourced example, not a throughput claim: Airtable documents 5 requests per second per base, plus 50 requests per second across bases for a given personal access token or service account. Exceed that and you get 429 and must wait 30 seconds before subsequent requests succeed (Airtable rate limits). Other vendors differ. Read their page. Do not copy this 5 into a Stripe graph.

n8n’s documented tools, in order of honesty:

ToolWhat it pacesWhat it does not
Loop Over Items + WaitSlices and pauses inside one executionWorker pool, other parents hitting the same base
HTTP Request Batching (Items per Batch, Batch Interval (ms))That HTTP nodeExecute Sub-workflow children that each contain their own HTTP
Retry On Fail + Wait Between TriesA failed requestA successful stampede that never 429s until the burst is over
Wait node in the childThat child graphSiblings started with Wait off
Worker --concurrencyBull jobs per worker in queue modeInline Execute Sub-workflow children

Procedure to size a batch without inventing a number:

  1. Open the vendor rate-limit page. Write the unit (per second / per minute, per base / per token).
  2. Count HTTP nodes in the child that hit that unit. Multiply by how many children you will allow to overlap.
  3. If that product exceeds the documented window, shrink Loop Batch Size or add Wait between loops until the product fits. Confirm with response headers if the vendor sends them, not with hope.
  4. Put Retry On Fail on the HTTP node as a backstop. Set Wait Between Tries longer than the window they document. Retry is not the pacer. The loop is.
  5. If two parents share the base, one of them owns the budget. The other queues or stops.

Wait-node detail that bites pacing: waits under 65 seconds stay in-process; longer waits offload execution data to the database (Wait). A 1-second pacer does not “free the worker.” It holds the process. That is fine for a bound loop. It is not a scale-out.

HTTP Request Batching is the in-node version of Loop + Wait for that HTTP node only. Add Option → Batching. Set Items per Batch and Batch Interval (ms). Example from n8n: if the API allows one request per second, interval 1000. That does nothing to sibling Execute Sub-workflow children. Each child is its own graph; each child’s HTTP node batches only its own items.

Where the HTTP livesPace it here
Inside one child, all-items ModeHTTP Batching or Wait in that child
Many children, per-item ModeLoop + Wait in the parent
Many parents, one baseOne owner; others stop or enqueue

Arithmetic check, not a benchmark: if each child makes 2 HTTP calls and you start 50 children at once, the vendor sees 100 overlapping calls from one Node process. Airtable’s 5/sec page does not care that your parent count is 1.

Rate limits are a vendor document plus overlapping child HTTP. They are not a worker flag.

Queue mode: same-worker overlap is not a worker pool

Queue mode is: main (or webhook processor) accepts the trigger, Redis holds the parent job, a worker pulls it, the worker loads the workflow from Postgres and runs it (enable queue mode). Worker --concurrency defaults to 10. n8n recommends 5 or higher; very low concurrency with many workers can exhaust the database connection pool. Share N8N_ENCRYPTION_KEY. Do not run queue mode on SQLite.

Execute Sub-workflow overlap still typically happens on that worker. Children get execution IDs. They usually do not become extra Redis jobs. Adding a fourth worker does not take the grandchild. The trap spoke is the measurement. This spoke is the design implication: simultaneous sub-workflows in queue mode are same-process overlap unless you change the start path.

Hedge: n8n renamed the node, shipped 2.x queue internals, and has changed how stop signals reach children. A later build could enqueue children. Until child execution IDs and Redis jobs rise together on a fan-out, assume inline. Confirm on the version string in Settings.

PatternOverlaps children?Spreads across workers?
Execute Sub-workflow, Wait on or offYes, if per-item and Wait offNo — typically same worker
Nested Execute treeYes, on the first workerNo
Parent HTTP → child production webhookYes, as separate production jobsYes — workers can pick them
Loop Over Items onlyNo — sequential slicesNo

Failure picture: five workers, queue depth looks fine, one worker pegged, four idle. You buy a sixth. The hot worker stays hot because the tree never left it.

  • You know whether the instance is regular mode or EXECUTIONS_MODE=queue
  • Main and every worker share encryption key and n8n version
  • You did not set queue mode to “get parallel sub-workflows”
  • If you need pool fan-out, the child starts with a published webhook
  • You left worker slots for those webhook children if the parent waits on HTTP

Quota note, not a reason to explode children: n8n counts only the parent toward execution quota; Execute Sub-workflow children, manual runs, and error-workflow runs do not increment it (understand executions). Cheap on the plan meter is not cheap on CPU or on Airtable.

Queue mode scales parent jobs. It does not magically farm Execute Sub-workflow children.

When should children start via webhook instead of Execute Workflow?

When you need the limiter and the worker pool to see the work. That is the pattern for distribution. It is a different product: each child is now an at-least-once production job, it counts toward quota, the parent HTTP can time out, and a retry can double-write unless the child claims a key first.

NeedExecute Sub-workflowWebhook child
Shared mapping, same worker is fineYesExtra failure surface
Return data into the parent this runYes (Wait on)Only with Wait/resume
Overlap on one processPer-item + Wait offUnnecessary
Cross-worker fan-outNo, typicallyYes
Enroll in production concurrencyNoYes
Simple credentials, one graphYesTwo published workflows to own

Procedure for a webhook child that will not double-write:

  1. Child starts with a Webhook node. Publish it. Production URL, not /webhook-test/.
  2. Claim an idempotency key from the business event before CRM / mail / charge.
  3. Return 200 on duplicate claims. A 500 invites another delivery.
  4. Attach the shared error workflow. Test it on an automatic fail.
  5. Parent sends only after it has a stable event ID. Do not POST a fresh UUID per attempt.
  6. Leave worker slots for children. If waiting parents can fill every --concurrency slot, children never run. That deadlock is the webhook-child version. Inline Execute children cannot deadlock that way; they can still melt the process.
  7. If you cannot do steps 2–4 this week, do not switch start paths. Batch the Execute call instead.

Execute Sub-workflow is the reuse primitive. Webhook is the distribution primitive. Pick the failure you can see.

What usually fails first when you go simultaneous?

The first failure is rarely a red parent. It is a vendor 429, a green parent whose children still write, or a Loop Over Items that still contains per-item Execute on the full list.

Concrete mode, no invented rates: a nightly parent loads a deal list, Execute Sub-workflow per deal, Wait off so it “stays fast.” One process overlaps every child HTTP. Airtable (or whoever) returns 429 and then a 30-second lockout on that base. The parent execution may already be success. Nobody is watching child Executions.

Second-place: Wait on, inner child loop returns early, two websites’ Sheet updates overlap, rows collide. The parent thought it serialized.

Third-place: switch to webhook children to “use the workers,” forget the key, queue retry double-creates the CRM row.

SymptomLikely causeWrong fixRight fix
429 at parent start timePer-item Wait off, no LoopRaise --concurrencyBatch + Wait; count child HTTP
Green parent, failed childrenWait off, error workflow only on parentSlack the parentHandler on the child
One worker 100% CPU, siblings idleExecute tree pinned to the parent workerAdd workersShallow the tree or webhook + keys
Duplicate CRM rows after the “fix”Webhook children retriedTurn Wait offKey before the write
Infinite loopLoop Over Items Reset without an exitRestart the instanceTermination condition

Do not page from parent status alone. Page from vendor 429s, child overlap, and whether the handler on the child fired.

A parent that finishes in two seconds is not a win if the children take the API with them.

How do I measure whether simultaneous sub-workflows are working?

Working means: child count matches the bound you named, overlap matches the pattern (Wait off + batch), vendor 429s are rare, and failures land on a handler a human will read. It does not mean “the parent was fast.”

MeterWhereHealthyBroken
Child execution countExecutions, under one parentEquals items or loop iterationsEquals the raw Sheet, or 0
OverlapChild start/finish timestamps≤ batch size (or 1 if serial)≈ full list, Wait off, no loop
Return dataParent node outputPresent if Wait onEmpty with Wait on (child branch issue)
Vendor 429 / Retry-AfterHTTP outputRare, boundedClusters at parent start
AlertsChild error workflowAutomatic fail delivers a linkOnly canvas clicks were tested
Worker CPU (queue mode)Host / containerMatches your expectation for one treeOne replica pegged after you “scaled”

Proof run (staging, throwaway, no customer writes):

  1. Child: Wait 15 seconds, no side effects. Save successful production executions.
  2. Parent: published webhook. Execute Sub-workflow, Run once for each item, 12 static items.
  3. Run A: Wait on. Record parent duration and whether children overlap.
  4. Run B: Wait off. Record parent duration vs last child finish. Parent should be allowed to finish first.
  5. Run C: Loop Over Items batch 3, Wait 2 seconds between loops, per-item Execute Wait off inside the loop. Child overlap should hug 3, not 12.
  6. Run D (only if you claim distribution): same 12 items via production webhooks. Workers may diversify. Execute path usually will not.

Do this on a published production URL. Manual Execute is a different class of run. A green canvas is not a load test.

  • Version string recorded (Settings → About)
  • Child overlap number written on the parent workflow
  • Wait on/off both proven once
  • Loop bound proven once
  • One automatic child fail posted a readable alert

No screenshot of overlap vs the number you intended, you have a story.

What should I skip if I only have a week?

Skip a queue-mode migration. Skip adding workers. Skip rewriting every helper into a webhook. Skip “true parallelism” debates. You have seven days to bound the fan-out and put alerts on the children.

Do this week:

  1. Inventory activated parents that call Execute Sub-workflow / Execute Workflow.
  2. For each, record Mode, Wait, item source, and whether the child writes.
  3. On any per-item child that writes: add Loop Over Items the same day. If you cannot batch, disable the parent until you can.
  4. Attach error workflows on parent and child. Prove one automatic fail.
  5. Put a Wait between batches on the tightest vendor. Read that vendor’s rate-limit page once.
  6. Run the 12-item Wait on/off/loop proof on staging. File the screenshot in the parent description.
This weekNext, if still hotLater
Name every fan-outProduction batch sizeWebhook children + keys
Wait on vs off, written downHTTP Batching inside the childSingle-consumer drain for one shared API
Error handler on the childPer-worker CPU if you are already in queue modeQueue mode, if regular mode is the actual bottleneck

A week that only “turns Wait off to go faster” is a week the vendor still sees the stampede. Change the bound.

When is this not worth doing yet — and when to hire?

Skip simultaneous children when the list is small, the child is a helper, and one all-items call with Wait on already finishes inside your window. Overlap is for independent units that are slow and bounded. It is not a default.

DIY when you can finish the inventory, the Loop ceiling, and the 12-item proof in an afternoon. Hire when a production parent already fans writes, you cannot name overlap, or you were about to buy workers to fix an Execute tree.

SituationDIYHire / $500 Automation Audit
Unpublished prototype, no customer writesYes — keep children as helpersNo
Three surface drafts from one extractYes — finite fan-out, human sign-offNo, unless publish is ungated
Production per-item child that writes CRM / mail / moneyBatch and key todayYes, if you cannot name overlap or recover duplicates
Queue mode, one hot worker, idle poolMeasure, then shallow or webhookYes, if you were about to buy more workers
“We set concurrency to 5” while children stampedeBound the graphYes, if the team thinks the flag is the governor

Spurlock Studios will not quote a fake share of instances that “need parallel sub-workflows.” Open Executions, count child rows under one parent, look at overlap, read the vendor 429 log. That count is the evidence.

If you DIY, still use the handbook defaults: idempotency before writes, a dead-letter path, an error workflow with an execution link. If you hire, buy blast-radius mapping — which parents fan, which children write, which Wait toggle you actually have — not a compose file with more replicas.

Adding workers to an Execute Sub-workflow tree is how you spend money to keep the same pin. Fix Mode, Wait, and batch size first.

FAQ

How to run multiple sub-workflows simultaneously?

Put the repeated graph in a child with an Execute Sub-workflow Trigger, call it with Execute Sub-workflow (Execute Workflow on older canvases), set Mode to Run once for each item, and turn Wait for Sub-Workflow Completion off when the parent does not need return data. Bound how many items exist with Loop Over Items (Split In Batches) plus a Wait. Worker --concurrency does not create this pattern and does not cap it.

How do I measure whether to run multiple sub-workflows simultaneously is working?

Count child execution rows under one published parent and compare overlap timestamps to the batch size you named. Wait off should allow the parent to finish before the last child; Wait on should return child data to the parent node. Vendor 429s clustering at parent start time mean the fan-out is still unbounded, even if the parent is green.

What usually fails first when teams try this?

Per-item Execute Sub-workflow with Wait off and no Loop Over Items, pointed at a real API. The parent looks fast; the vendor returns 429. Second place: error workflow only on the parent, so fire-and-forget children fail silently. Third: rewriting children as webhooks without idempotency and double-writing on retry.

How long does this take to show results?

The staging proof — 12 static items, Wait on, Wait off, then a Loop batch — is one published parent/child pair, usually hours. Putting a batch ceiling on a production fan-out is the same day. Reconstructing which child writes already landed, if you never stored keys, takes longer. That reconstruction is the cost of the unbounded tree, not the cost of the measurement.

What should I skip if I only have a week?

Skip queue-mode builds, extra workers, and a full webhook rewrite. Do not skip: inventory of Execute Sub-workflow parents, a Loop ceiling on any per-item child that writes, error-workflow attach on the child, and the Wait on/off/loop proof. Leave helper children that do no HTTP on all-items Mode.

When is this not worth doing yet?

When the child is a mapper, item counts are small, and one all-items call with Wait on already finishes in time. The moment an activated parent fans child writes with per-item Mode and Wait off, you already have simultaneous sub-workflows — you just have not bounded them. A prototype can over-parallelize in the editor. Production needs a named batch size before it needs more workers.

CTA

Overlap is Mode plus Wait. The bound is the loop. The vendor does not care about your parent slot.

Read the Production n8n handbook, then use automation or book the $500 Automation Audit.

FAQ

What questions does this article answer?

How to run multiple sub-workflows simultaneously?
Put the repeated graph in a child with an Execute Sub-workflow Trigger, call it with Execute Sub-workflow (Execute Workflow on older canvases), set Mode to Run once for each item, and turn Wait for Sub-Workflow Completion off when the parent does not need return data. Bound how many items exist with Loop Over Items (Split In Batches) plus a Wait. Worker `--concurrency` does not create this pattern and does not cap it.
How do I measure whether to run multiple sub-workflows simultaneously is working?
Count child execution rows under one published parent and compare overlap timestamps to the batch size you named. Wait off should allow the parent to finish before the last child; Wait on should return child data to the parent node. Vendor 429s clustering at parent start time mean the fan-out is still unbounded, even if the parent is green.
What usually fails first when teams try this?
Per-item Execute Sub-workflow with Wait off and no Loop Over Items, pointed at a real API. The parent looks fast; the vendor returns 429. Second place: error workflow only on the parent, so fire-and-forget children fail silently. Third: rewriting children as webhooks without idempotency and double-writing on retry.
How long does this take to show results?
The staging proof — 12 static items, Wait on, Wait off, then a Loop batch — is one published parent/child pair, usually hours. Putting a batch ceiling on a production fan-out is the same day. Reconstructing which child writes already landed, if you never stored keys, takes longer. That reconstruction is the cost of the unbounded tree, not the cost of the measurement.
What should I skip if I only have a week?
Skip queue-mode builds, extra workers, and a full webhook rewrite. Do not skip: inventory of Execute Sub-workflow parents, a Loop ceiling on any per-item child that writes, error-workflow attach on the child, and the Wait on/off/loop proof. Leave helper children that do no HTTP on all-items Mode.
When is this not worth doing yet?
When the child is a mapper, item counts are small, and one all-items call with Wait on already finishes in time. The moment an activated parent fans child writes with per-item Mode and Wait off, you already have simultaneous sub-workflows — you just have not bounded them. A prototype can over-parallelize in the editor. Production needs a named batch size before it needs more workers.
Sources

Last reviewed

More from this lane

Automation

All →
Book the audit