> For the complete documentation index, see [llms.txt](https://developer.harness.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.harness.io/release-orchestration/use-release-orchestration/release-orchestration-phases/phase-looping.md).

# Loop phases and activities

Run a phase or activity multiple times across different targets using looping support in Release Orchestration

{% hint style="success" %}
This feature is in Limited GA and requires enablement. [View all early access features.](/release-notes/features.md)

{% @harness-feature-flag-access/request-feature-flag-access module="release-orchestration" pagePath="release-orchestration/use-release-orchestration/release-orchestration-phases/phase-looping" featureFlags="%5B%7B%22name%22%3A%22Looping%20Support%22%2C%22key%22%3A%22RMG\_LOOPING\_ENABLED%22%2C%22status%22%3A%22LIMITED\_GA%22%2C%22description%22%3A%22Enables%20looping%20for%20phases%20and%20activities%20in%20Release%20Orchestration%2C%20allowing%20them%20to%20execute%20across%20multiple%20targets%20in%20a%20single%20release.%22%7D%2C%7B%22name%22%3A%22Dynamic%20Queue%20Activity%20Targets%22%2C%22key%22%3A%22RMG\_ENABLE\_WEBHOOK\_QUEUE\_SUPPORT%22%2C%22status%22%3A%22LIMITED\_GA%22%2C%22description%22%3A%22Enables%20selecting%20the%20Pipeline%20Queue%20activity%20in%20the%20Looping%20Strategy%20panel's%20Queue%20Activity%20Output%20tab%20by%20expression%2C%20so%20the%20activity%20a%20loop%20reads%20from%20can%20resolve%20at%20runtime.%22%7D%5D" %}
{% endhint %}

Looping lets you run a phase, or a single activity within a phase, once for each target in a list. Instead of duplicating phases or activities to deploy across multiple services, environments, or regions, you define the phase or activity once, attach a target list, and Release Orchestration automatically spreads the execution.

## What you will learn from this topic

* How to add a `targets` block with typed target fields to a phase or to an individual activity.
* How to reference the current iteration's target field values from an activity.
* How to drive a loop from a dynamic list, including values produced by another phase's activity.
* How to view, retry, and ignore individual loop iterations during execution.

## Before you begin

Confirm that you understand the following concepts before configuring looping for a phase or an activity.

* Go to the [Phase overview](/release-orchestration/use-release-orchestration/release-orchestration-phases/phases-overview.md) to understand phases and how they structure a release process.
* Go to the [Activities overview](/release-orchestration/use-release-orchestration/release-orchestration-activities/activities-overview.md) to understand how activities work within phases.
* Go to the [Creating phases](/release-orchestration/use-release-orchestration/release-orchestration-phases/creating-phases.md) to learn how to define phases in a process.

## How looping works

When you add a `targets` block to a phase or an individual activity, Release Orchestration creates one child execution node per target at runtime. A looped phase runs all of its activities independently for each target. A looped activity inside a non-looped phase runs only that activity once per target, while the rest of the phase's activities run once as usual. Either way, the current target's field values are exposed to activities as an expression they can reference in their pipeline inputs.

The execution graph shows the looped phase or activity as a single parent node. You can expand it horizontally to see all child executions and their individual status. This keeps the graph readable even for large target lists.

Child executions run independently. A failure in one does not stop or fail the others; they continue to run to completion on their own. However, the looped phase or activity, and any phase that depends on it, does not complete until every child execution reaches Success or Ignored. A single unresolved failure holds up progress, even if every other child in the loop succeeded, so you must either retry the failed child until it succeeds or ignore it before the process can move on.

## Configure a looping phase

You can define the loop target list in two ways: a static list of hardcoded values, or a dynamic list built from the accumulated output of a Pipeline Queue activity. Both methods work whether you loop a whole phase or a single activity within a phase, and you can configure either method from YAML or from the **Looping Strategy** panel in the UI.

### Define a static target list

To loop a phase over a fixed set of values, add a `targets` block directly to the phase definition in your process YAML. The `targets` object declares one or more typed fields under `fields`, then lists the values for each field under `values`. Release Orchestration runs one child execution for every combination of the field values.

<details>

<summary>YAML example: static targets on a phase</summary>

```yaml
process:
  id: LoopingProcess
  name: LoopingProcess
  phases:
    - phase:
        id: LoopedPhase
        name: LoopedPhase
        depends-on:
          - PhaseActivityLoop
        targets:
          fields:
            service:
              type: string
            environment:
              type: string
          values:
            - service:
                - rmg
                - ngui
                - ng
            - environment:
                - qa
                - prod0
                - prod1
        activities:
          - activity: PipeRunnable
            id: PipeRunnable
            name: PipeRunnable
```

</details>

In this example, Release Orchestration runs the `LoopedPhase` phase once for every combination of `service` and `environment`, nine child executions in total.

### Loop an individual activity instead of the whole phase

You can add a `targets` block to a single activity instead of the phase. Release Orchestration then loops only that activity, once per target combination, while the rest of the phase's activities run once.

<details>

<summary>YAML example: static targets on an activity</summary>

```yaml
phases:
  - phase:
      id: PhaseActivityLoop
      name: PhaseActivityLoop
      depends-on:
        - NoLoopPhase
      activities:
        - activity: PipeRunnable
          id: PipeRunnable
          name: PipeRunnable
          targets:
            fields:
              environment:
                type: string
            values:
              - environment:
                  - qa
                  - prod0
                  - prod1
```

</details>

A phase and one of its activities can each define an independent `targets` block. When both are present, the phase loops over its own targets, and the looped activity loops over its own targets within each phase iteration.

### Reference the loop target in activities

Inside a looped phase or a looped activity, activities can access the current iteration's field values using the expression `<+target.<field>>`, where `<field>` is one of the field names declared under the enclosing `targets.fields`. This expression is used in the pipeline input set, which is part of the activity's `pipeline.inputSet.variables` block that maps values to the underlying pipeline's variables, not in the activity's own `inputs` declaration.

<details>

<summary>YAML example: referencing loop target fields in a pipeline input set</summary>

```yaml
activity:
  id: PipeRunnable
  name: PipeRunnable
  pipeline:
    pipeline: org/project/PipelineName
    inputSet:
      variables:
        - name: service
          type: String
          value: <+target.service>
        - name: environment
          type: String
          value: <+target.environment>
```

</details>

### Drive a loop from a Pipeline Queue activity's output

Instead of hard-coding values under `targets.values`, you can set `values` to an expression that resolves to a list at runtime. This is useful when the set of targets changes between releases or comes from pipeline runs that happened earlier in the process.

This method has two parts: a Pipeline Queue activity that accumulates values from pipeline runs, and a `targets` block elsewhere in the process that consumes those accumulated values.

Perform the following steps to accumulate values in a queue:

1. In the pipeline you want to monitor, add a Publish Events step and report the values you want to loop on as metadata key-value pairs, for example `service` , or `environment`. Go to [Add the Publish Events step to your build pipeline](/release-orchestration/use-release-orchestration/release-orchestration-artifacts/artifact-tracker-getting-started.md#add-the-ro-notify-step-to-your-build-pipeline) for details on configuring the Publish Events step.
2. In your process YAML, add a Pipeline Queue activity to a phase and select the pipeline to monitor.
3. Add an output variable on the Pipeline Queue activity for each metadata key you reported in the Publish Events step, and set its value to `<+metadata.<keyname>>`, where `<keyname>` is the metadata key, for example `<+metadata.service>`.

Every time the monitored pipeline runs, Release Orchestration adds one entry to the queue activity's output list with the values from that run.

Perform the following steps to consume the accumulated values as loop targets:

1. On the phase or activity you want to loop, add a `targets` block.
2. Declare the fields you expect in each queue entry under `fields`.
3. Set `values` to an expression that references the queue activity's output list, for example `<+process.NoLoopPhase.queueActivity.output.values>`.

<details>

<summary>YAML example: looping over a Pipeline Queue activity's accumulated output</summary>

```yaml
phases:
  - phase:
      id: PhaseActivityLoop
      name: PhaseActivityLoop
      depends-on:
        - NoLoopPhase
      activities:
        - activity: PipeRunnable
          id: PipeRunnable
          name: PipeRunnable
          targets:
            fields:
              env:
                type: string
              svc:
                type: string
            values: <+process.NoLoopPhase.queueActivity.output.values>
```

</details>

At release time, Release Orchestration resolves the `values` expression and creates one child execution per item it returns. This example reads the list from `queueActivity`, a Pipeline Queue activity in the `NoLoopPhase` phase.

## View looped executions

In the execution graph, a looped phase appears as a single node labeled with the phase name and a **Loop** badge.

To see individual child executions, open the **Phases** panel and expand the looped phase entry. Release Orchestration lists one child entry per target, named `<phase-id>_<index>` (for example, `LoopedPhase_0`, `LoopedPhase_1`). Select a child entry to load its own activity graph and status on the right, without leaving the release view.

![Phases panel with a looped phase expanded into its child executions](https://478177610-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FwRDYqBolZUj6Rwnqx0mm%2Fuploads%2Fgit-blob-09fdc42038361bbd7bfe55fe168f769420ab9ff8%2Flooping-strategy-steps.png?alt=media)

## Retry and ignore individual loop iterations

The other child executions in a loop keep running even after one of them fails, but the looped phase or activity itself does not complete until every child reaches Success or Ignored. When a child's execution fails, you have two options:

* **Retry**: Re-run the failed child execution from the beginning. Use this when the failure was transient or when you have fixed an underlying issue. The retry runs with the same target value and inputs as the original attempt.
* **Ignore**: Mark the child execution as ignored so it no longer holds up completion of the looped phase or activity. The ignored child is recorded in the execution history for traceability.

All child executions in the loop must reach Success or Ignored for the looped phase or activity to complete. It does not matter whether a given child succeeded or was ignored, as long as none are left in a failed state.

To access these actions, expand the looped phase node in the execution graph, select the failed child execution, and select **Retry** or **Ignore** from the action menu.

Go to [Error handling](/release-orchestration/use-release-orchestration/release-orchestration-execution/error-handling.md) for more details on retry and ignore behavior across all activity types.

## Looping strategy panel

When you edit a phase or an activity through the UI, expand the **Looping Strategy** section to configure looping without editing YAML. The panel has two tabs, matching the two ways to define a target list described in [Configure a looping phase](#configure-a-looping-phase).

**Add Targets**: Configure a static target list. Click **New Target** to add a target, then add one or more fields and their values. Release Orchestration runs one child execution for each combination of the field values you provide and shows the total under **Total Phase Iterations**. You can add, reorder, or remove fields and values directly in this tab.

![Add Targets tab in the Looping Strategy panel](https://478177610-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FwRDYqBolZUj6Rwnqx0mm%2Fuploads%2Fgit-blob-2050a4e13f8cc6181be494c52f5f8185083058c2%2Flooping-strategy-add-targets.png?alt=media)

**Queue Activity Output**: Configure a dynamic target list. Select the phase and Pipeline Queue activity to read values from under **Select Activity Queue**, then add one or more fields under the **Data schema of the resolved expression** that match the output variables on that queue activity. Field names you add here become the loop target fields you reference with `<+target.<field>>`.

**Select Activity Queue** accepts either a fixed Pipeline Queue activity or an expression that resolves to one at runtime, so the queue activity a loop reads from can vary by execution instead of being hardcoded. Resolving the queue activity from an expression requires the feature flag `RMG_ENABLE_WEBHOOK_QUEUE_SUPPORT`, in addition to `RMG_LOOPING_ENABLED`.

![Queue Activity Output tab in the Looping Strategy panel](https://478177610-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FwRDYqBolZUj6Rwnqx0mm%2Fuploads%2Fgit-blob-0bf9253aea5602b25a001933f516ee707eab4d41%2Flooping-strategy-queue-activity-output.png?alt=media)

## Important notes

Keep the following behavior in mind when you work with looped phases and activities.

* You can loop a phase, an individual activity within a phase, or both at once. A phase-level loop and an activity-level loop inside it run independently, and their target lists do not need to match.
* In a looped phase, an activity without its own `targets` block executes for every phase-level target. There is no way to skip a specific activity for a specific target within the same phase.
* A looped phase or activity completes only after every child's execution reaches Success or Ignored. A single failed child does not stop its siblings from running, but it does hold up the phase or activity and anything that depends on it until you retry it successfully or ignore it.

## Next steps

* [Phase dependencies](/release-orchestration/use-release-orchestration/release-orchestration-phases/phase-dependencies.md): Learn how to sequence phases that a looped phase depends on, or that depend on it.
* [Error handling](/release-orchestration/use-release-orchestration/release-orchestration-execution/error-handling.md): Understand retry and ignore behavior across all activity types, not only looped ones.
* [Subprocess activities](/release-orchestration/use-release-orchestration/release-orchestration-activities/activity-types/subprocess-activities.md): Reference activities you can loop alongside `ReleasePipeline` and `PipelineQueue`.
* [Supported expressions](/release-orchestration/use-release-orchestration/release-orchestration-inputs-variables/supported-expressions.md): Look up other expressions you can combine with `<+target.<field>>` in a looped activity.
