
ForEach
Execute a group of tasks for each value in the list.
Execute a group of tasks for each value in the list.
Execute a group of tasks for each value in the list.
You can control how many task groups are executed concurrently by setting the concurrencyLimit property.
- A
concurrencyLimitof0means no limit — all task groups run in parallel. - A
concurrencyLimitof1means full serialization — only one task group runs at a time, in order. - A
concurrencyLimitgreater than1allows up to that number of task groups to run in parallel.
Regardless of the concurrencyLimit property, the tasks will run one after the other — to run those in parallel, wrap them in a Parallel task as shown in the last example below (see the flow parallel_tasks_example).
The values can be defined as a JSON string or an array, e.g. a list of string values ["value1", "value2"] or a list of key-value pairs [{"key": "value1"}, {"key": "value2"}].
Access the current iteration value using {{ taskrun.value }} or {{ parent.taskrun.value }} when inside a nested child task. The iteration number is available via {{ taskrun.iteration }}.
If you need to execute more than 2-5 tasks for each value, we recommend triggering a subflow for each value for better performance and modularity. See the flow best practices documentation for more details.
type: "io.kestra.plugin.core.flow.ForEach"Examples
The {{ taskrun.value }} from the for_each task is available only to direct child tasks such as the before_if and the if tasks. To access the taskrun value of the parent task in a nested child task such as the after_if task, use {{ parent.taskrun.value }}.
id: for_loop_example
namespace: company.team
tasks:
- id: for_each
type: io.kestra.plugin.core.flow.ForEach
values: ["value 1", "value 2", "value 3"]
tasks:
- id: before_if
type: io.kestra.plugin.core.debug.Return
format: "Before if {{ taskrun.value }}"
- id: if
type: io.kestra.plugin.core.flow.If
condition: '{{ taskrun.value == "value 2" }}'
then:
- id: after_if
type: io.kestra.plugin.core.debug.Return
format: "After if {{ parent.taskrun.value }}"
This flow uses YAML-style array for values. The task for_each iterates over a list of values and executes the return child task for each value. The concurrencyLimit property is set to 2, so the return task will run concurrently for the first two values in the list at first. The return task will run for the next two values only after the task runs for the first two values have completed.
id: for_each_value
namespace: company.team
tasks:
- id: for_each
type: io.kestra.plugin.core.flow.ForEach
values:
- value 1
- value 2
- value 3
- value 4
concurrencyLimit: 2
tasks:
- id: return
type: io.kestra.plugin.core.debug.Return
format: "{{ task.id }} with value {{ taskrun.value }}"
This example shows how to run tasks in parallel for each value in the list. All child tasks of the parallel task will run in parallel. However, due to the concurrencyLimit property set to 2, only two parallel task groups will run at any given time.
id: parallel_tasks_example
namespace: company.team
tasks:
- id: for_each
type: io.kestra.plugin.core.flow.ForEach
values: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
concurrencyLimit: 2
tasks:
- id: parallel
type: io.kestra.plugin.core.flow.Parallel
tasks:
- id: log
type: io.kestra.plugin.core.log.Log
message: Processing {{ parent.taskrun.value }}
- id: shell
type: io.kestra.plugin.scripts.shell.Commands
commands:
- sleep {{ parent.taskrun.value }}
This example demonstrates processing data across nested loops of S3 buckets, years, and months. It generates structured identifiers (e.g., bucket1_2025_March) by combining values from each loop level, while accessing parent loop values like years and buckets, which can be useful for partitioned storage paths or time-based datasets. The flow uses dynamic expressions referencing parent context.
id: loop_multiple_times
namespace: company.team
inputs:
- id: s3_buckets
type: ARRAY
itemType: STRING
defaults:
- bucket1
- bucket2
- id: years
type: ARRAY
itemType: INT
defaults:
- 2025
- 2026
- id: months
type: ARRAY
itemType: STRING
defaults:
- March
- April
tasks:
- id: buckets
type: io.kestra.plugin.core.flow.ForEach
values: "{{inputs.s3_buckets}}"
tasks:
- id: year
type: io.kestra.plugin.core.flow.ForEach
values: "{{inputs.years}}"
tasks:
- id: month
type: io.kestra.plugin.core.flow.ForEach
values: "{{inputs.months}}"
tasks:
- id: full_table_name
type: io.kestra.plugin.core.log.Log
message: |
Full table name: {{parents[1].taskrun.value }}_{{parent.taskrun.value}}_{{taskrun.value}}
Direct/current loop (months): {{taskrun.value}}
Value of loop one higher up (years): {{parents[0].taskrun.value}}
Further up (table types): {{parents[1].taskrun.value}}
Properties
tasks*RequiredNon-dynamicarray
1values*Requiredstringarray
The list of values for which Kestra will execute a group of tasks
The values can be passed as a string, a list of strings, or a list of objects.
concurrencyLimitNon-dynamicinteger
1>= 0The number of concurrent task groups for each value in the values array
A concurrencyLimit of 0 means no limit — all task groups run in parallel.
A concurrencyLimit of 1 means full serialization — only one task group runs at a time, in order.
A concurrencyLimit greater than 1 allows up to the specified number of task groups to run in parallel.
errorsNon-dynamicarray
List of tasks to run if any tasks failed on this FlowableTask.