How to run multiple sub-workflows simultaneously
Turn Wait off on Execute Sub-workflow in per-item mode to overlap children. Bound that fan-out with Loop Over Items plus a Wait; worker flags will not cap it.
William Spurlock Founder — Spurlock Studios 28 MIN
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.
--concurrencydoes not see these children.
| Goal | Graph | Not the graph |
|---|---|---|
| Reuse a mapper, one result set | One child, Run once with all items, Wait on | Per-item fan-out |
| Overlap independent items | Per-item Mode, Wait off, named max items | Unbounded Sheet → child |
| Pace a tight API | Loop Over Items → one child per slice → Wait | Per-item with Wait off and no ceiling |
| Spread work across workers | Child starts on a published webhook | Execute 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:
- Create the child. Add Execute Sub-workflow Trigger. Set Input data mode to Define using fields below (or a JSON example). Save.
- In child Settings, set This workflow can be called by to the parent you are about to wire — not “anyone.”
- In child Settings, attach the shared error workflow. Fire-and-forget children fail after the parent is gone. See error workflows operators actually read.
- In the parent, add Execute Sub-workflow. Source: Database, From list. Map the child’s fields.
- Set Mode to Run once for each item.
- Open Options. Turn Wait for Sub-Workflow Completion off if you want overlap. Leave it on if the next parent node needs child output.
- Put Loop Over Items in front of that node with a batch size you can defend. Name max items per parent run.
- Publish both. Fire the parent from a production webhook or schedule. Do not treat an editor Execute click as the proof.
| Piece | Healthy default | Failure if you skip it |
|---|---|---|
| Child trigger | When Executed by Another Workflow | Parent has nothing to call |
| Input contract | Named fields / JSON example | Accept all data into a write |
| Callers | One named parent | Two parents, one unbounded child |
| Mode | Per-item only after the list is bounded | One child per Sheet row, forever |
| Wait | On for return data; off for launch | Green parent, children still writing |
| Proof | Published parent, overlapping child timestamps | Canvas 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:
| Parameter | What you set | What we actually use |
|---|---|---|
| Source | Database, Local File, Parameter (JSON), URL | Database / From list for production |
| Workflow Inputs | Fields pulled from the child’s trigger | Mapped, typed, no surprise nulls into writes |
| Mode | All items, or each item | All items for reduce/map; each item for independent units |
| Wait for Sub-Workflow Completion | On = parent pauses; off = parent continues | On 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:
- Production parent/child in the same instance → Database, pick from the list. IDs drift less than pasted JSON.
- Local File / Parameter / URL → throwaway or codegen. Not the production caller.
- 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 mode | Parent sees | Use when |
|---|---|---|
| Define using fields below | Named, typed inputs on Execute Sub-workflow | Production writes. Default. |
| Define using JSON example | Shape inferred from the example object | You already have a payload fixture |
| Accept all data | No required fields; child must tolerate mess | Prototypes 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.
| Wait | Parent | Child executions | Use when |
|---|---|---|---|
| On (typical default) | Holds until that child returns | You get last-node data back | Enrichment, validation, anything the next node reads |
| Off (fire-and-forget) | Continues; may finish while children run | Still start; no return on this branch | Notify, 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:
- Does a downstream parent node read child fields? Wait on.
- If the child fails, must the parent fail? Wait on, and do not Continue on Fail on the Execute node.
- Is the child a background job the parent will not join? Wait off, error workflow on the child, idempotency in the child.
- If you cannot answer 1–3, leave Wait on. Launch-and-hope is how a green parent hides a 429.
| Parent Wait | Child later fails | Who sees it |
|---|---|---|
| On | Child error can fail the parent | Parent error workflow, if attached and the run is automatic |
| Off | Parent already succeeded | Child error workflow only |
| On + Continue on Fail | Child error swallowed | Often 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:
- Child writes a status row (Airtable / Postgres / Data Table) keyed by the business event before it finishes.
- 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. - Or you do not join. The child files
needs_reviewand 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.
| Mode | Child executions per parent run | Honest use |
|---|---|---|
| Run once with all items | One | Mapping, reduce, shared lookup, one report |
| Run once for each item | One per item | Independent 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 list | Mode | Wait | What you should see |
|---|---|---|---|
| 20 rows | All items | On | 1 child, parent holds |
| 20 rows | Each item | On | 20 children; parent waits on the node (serialize unless your build overlaps anyway — measure) |
| 20 rows | Each item | Off | 20 children overlapping unless something else paces them |
| 20 rows, Loop batch 5, one child per loop, Wait between loops | All items on the slice | On | 4 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.
| Mechanism | What runs at once | What it is for |
|---|---|---|
| Normal item processing (no loop node) | One execution, node walks the list | Default. Not multiple sub-workflows |
Loop Over Items, Batch Size N | One slice, then the next | Pace. Rate limits. Pagination |
| Execute Sub-workflow, all items | One child execution | Reuse a graph on the whole list |
| Execute Sub-workflow, per item, Wait off | Many child executions, overlapping | Actual simultaneous sub-workflows |
| Loop + per-item Execute inside the loop | Still ~list-length children if you per-item the slice | Easy 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):
- Read the child’s contract. One item in, or a list in?
- Add Loop Over Items in the parent. Start Batch Size smaller than you want. You are buying a bound.
- 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.
- After the child (or after the HTTP inside the child), add Wait. Connect back to the loop.
- Count child rows on the next production parent. They should match loop iterations (or iterations × slice size), not the raw list.
| If you want | Put this in the parent |
|---|---|
| True overlap, bounded | Loop 5 → per-item Execute, Wait off, Wait between loops |
| Serial safety | Loop 1 → one Execute, Wait on, Wait after |
| One child, internal HTTP pacing | All-items Execute; HTTP Batching or Wait inside the child |
| Cross-worker spread | Do 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:
| Expression | Returns | Use |
|---|---|---|
{{$("Loop Over Items").context["noItemsLeft"]}} | boolean | true when the node has no remaining slice |
{{$("Loop Over Items").context["currentRunIndex"]}} | number | Which 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:
- Helper, no HTTP. One child, all items. Stop. You do not have a simultaneity problem.
- Independent reads. Per-item + Wait off, Loop batch you measured, Wait between batches.
- Independent writes (CRM, mail, charge). Same as 2, plus idempotency in the child, error workflow on the child, fail closed.
- 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.
- Need the worker pool. Stop using Execute Sub-workflow as the start path. Published webhook on the child.
| Fan-out | Child count | Bound |
|---|---|---|
| One extract → 3 surface children | 3 | Surface list is finite |
| Sheet of N leads → per-lead enrich | N | Pagination / hard slice / Loop |
| Nested parent → child → grandchild via Execute | Tree | Depth 1 on the hot path |
| Two published parents, one child | Sum of both lists | Callers setting |
Procedure we use when a parent already fans in production:
- Freeze new per-item Execute calls.
- Count last night’s parent: child rows, overlap window, whether the child writes.
- Insert Loop Over Items. Batch size starts small.
- Move writes behind a key in the child.
- Attach error workflows on parent and child. Prove one automatic fail — editor Execute does not fire Error Trigger.
- 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:
| Tool | What it paces | What it does not |
|---|---|---|
| Loop Over Items + Wait | Slices and pauses inside one execution | Worker pool, other parents hitting the same base |
HTTP Request Batching (Items per Batch, Batch Interval (ms)) | That HTTP node | Execute Sub-workflow children that each contain their own HTTP |
| Retry On Fail + Wait Between Tries | A failed request | A successful stampede that never 429s until the burst is over |
| Wait node in the child | That child graph | Siblings started with Wait off |
Worker --concurrency | Bull jobs per worker in queue mode | Inline Execute Sub-workflow children |
Procedure to size a batch without inventing a number:
- Open the vendor rate-limit page. Write the unit (per second / per minute, per base / per token).
- Count HTTP nodes in the child that hit that unit. Multiply by how many children you will allow to overlap.
- 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.
- 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.
- 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 lives | Pace it here |
|---|---|
| Inside one child, all-items Mode | HTTP Batching or Wait in that child |
| Many children, per-item Mode | Loop + Wait in the parent |
| Many parents, one base | One 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.
| Pattern | Overlaps children? | Spreads across workers? |
|---|---|---|
| Execute Sub-workflow, Wait on or off | Yes, if per-item and Wait off | No — typically same worker |
| Nested Execute tree | Yes, on the first worker | No |
| Parent HTTP → child production webhook | Yes, as separate production jobs | Yes — workers can pick them |
| Loop Over Items only | No — sequential slices | No |
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.
| Need | Execute Sub-workflow | Webhook child |
|---|---|---|
| Shared mapping, same worker is fine | Yes | Extra failure surface |
| Return data into the parent this run | Yes (Wait on) | Only with Wait/resume |
| Overlap on one process | Per-item + Wait off | Unnecessary |
| Cross-worker fan-out | No, typically | Yes |
| Enroll in production concurrency | No | Yes |
| Simple credentials, one graph | Yes | Two published workflows to own |
Procedure for a webhook child that will not double-write:
- Child starts with a Webhook node. Publish it. Production URL, not
/webhook-test/. - Claim an idempotency key from the business event before CRM / mail / charge.
- Return 200 on duplicate claims. A 500 invites another delivery.
- Attach the shared error workflow. Test it on an automatic fail.
- Parent sends only after it has a stable event ID. Do not POST a fresh UUID per attempt.
- Leave worker slots for children. If waiting parents can fill every
--concurrencyslot, children never run. That deadlock is the webhook-child version. Inline Execute children cannot deadlock that way; they can still melt the process. - 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.
| Symptom | Likely cause | Wrong fix | Right fix |
|---|---|---|---|
| 429 at parent start time | Per-item Wait off, no Loop | Raise --concurrency | Batch + Wait; count child HTTP |
| Green parent, failed children | Wait off, error workflow only on parent | Slack the parent | Handler on the child |
| One worker 100% CPU, siblings idle | Execute tree pinned to the parent worker | Add workers | Shallow the tree or webhook + keys |
| Duplicate CRM rows after the “fix” | Webhook children retried | Turn Wait off | Key before the write |
| Infinite loop | Loop Over Items Reset without an exit | Restart the instance | Termination 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.”
| Meter | Where | Healthy | Broken |
|---|---|---|---|
| Child execution count | Executions, under one parent | Equals items or loop iterations | Equals the raw Sheet, or 0 |
| Overlap | Child start/finish timestamps | ≤ batch size (or 1 if serial) | ≈ full list, Wait off, no loop |
| Return data | Parent node output | Present if Wait on | Empty with Wait on (child branch issue) |
| Vendor 429 / Retry-After | HTTP output | Rare, bounded | Clusters at parent start |
| Alerts | Child error workflow | Automatic fail delivers a link | Only canvas clicks were tested |
| Worker CPU (queue mode) | Host / container | Matches your expectation for one tree | One replica pegged after you “scaled” |
Proof run (staging, throwaway, no customer writes):
- Child: Wait 15 seconds, no side effects. Save successful production executions.
- Parent: published webhook. Execute Sub-workflow, Run once for each item, 12 static items.
- Run A: Wait on. Record parent duration and whether children overlap.
- Run B: Wait off. Record parent duration vs last child finish. Parent should be allowed to finish first.
- 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.
- 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:
- Inventory activated parents that call Execute Sub-workflow / Execute Workflow.
- For each, record Mode, Wait, item source, and whether the child writes.
- On any per-item child that writes: add Loop Over Items the same day. If you cannot batch, disable the parent until you can.
- Attach error workflows on parent and child. Prove one automatic fail.
- Put a Wait between batches on the tightest vendor. Read that vendor’s rate-limit page once.
- Run the 12-item Wait on/off/loop proof on staging. File the screenshot in the parent description.
| This week | Next, if still hot | Later |
|---|---|---|
| Name every fan-out | Production batch size | Webhook children + keys |
| Wait on vs off, written down | HTTP Batching inside the child | Single-consumer drain for one shared API |
| Error handler on the child | Per-worker CPU if you are already in queue mode | Queue 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.
| Situation | DIY | Hire / $500 Automation Audit |
|---|---|---|
| Unpublished prototype, no customer writes | Yes — keep children as helpers | No |
| Three surface drafts from one extract | Yes — finite fan-out, human sign-off | No, unless publish is ungated |
| Production per-item child that writes CRM / mail / money | Batch and key today | Yes, if you cannot name overlap or recover duplicates |
| Queue mode, one hot worker, idle pool | Measure, then shallow or webhook | Yes, if you were about to buy more workers |
| “We set concurrency to 5” while children stampede | Bound the graph | Yes, 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.
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.
Last reviewed
Automation
Automation After the show is not you at 1 a.m.
Post-show onboarding — thank-you, join path, merch nudge — belongs in a human-gated n8n rail, not your thumb at load-out.
Automation Paperwork that is not the plant
Invoice and PO matching, intake, and support triage in n8n with Metrc fences — the paperwork operators hate, not a menu widget.
Automation Saturday still books — the missed-call rail for trades
A missed-call text-back that routes zip and books a slot beats voicemail and Saturday desk coverage you cannot keep staffed. If a kid is cheaper, say so.
Automation When does Continue on Fail hide real API errors in n8n
Continue on Fail hides real API errors when the node fails but the run stays green. Error Workflow never fires; last-valid data often walks into the next write.
Will's Journal in your inbox.
What I learned this week building for shops, floors, and houses.
You're on the list.
Sign-up failed — try again.
By subscribing, you agree to the Privacy Policy.