> 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/continuous-delivery/use-gitops/gitops-entities/service/migrate-deployment-repo-to-appset-references.md).

# Migrate Deployment Repo to Application Set references

Move GitOps services from the deprecated Deployment Repository manifest to Application Set references (appsetConfigs) so Fetch Linked Apps and related pipelines keep working.

{% hint style="warning" %}
**Deployment Repository is being deprecated**

The **Deployment Repository** manifest on GitOps services is being deprecated and will stop working on **October 4, 2026**. If your service still uses a Deployment Repository, follow this guide to migrate to **Application Set references** before then so that Fetch Linked Apps and PR pipelines keep working.
{% endhint %}

A Deployment Repository tells Harness **where to look** for an ApplicationSet: a connector, a branch or commit, and a file path. At runtime, Fetch Linked Apps fetched that file from git and read its `metadata.name`. Application Set references store the answer instead of the lookup. Each reference names one ApplicationSet directly, along with the GitOps agent that manages it, so Harness no longer reads git to learn which ApplicationSet a service deploys.

This guide shows you how to find the right names and agents for your service, apply the change through the UI, YAML, the API, or Terraform, and confirm that your pipelines still return the same applications.

## What you will learn from this topic

* How to [choose the right migration path](#choose-a-migration-path) for your service
* How to [identify Application Set names and agents](#identify-application-set-names-and-agents) from git or from live GitOps inventory
* How to [apply the migration](#apply-the-migration) through the UI, YAML, the API, or Terraform
* How to [handle expressions and runtime inputs](#handle-expressions-and-runtime-inputs) when the Deployment Repository store is dynamic
* How to [validate the migration](#validate-the-migration) and what happens after [Deployment Repository removal](#deployment-repository-removal)

## Before you begin

* **Service edit access:** Create or Edit permission on the GitOps service you are migrating. Go to the [permissions reference](/harness-ai/use-harness-platform/platform-access-control/permissions-reference.md#services) to review required roles.
* **GitOps visibility:** View permission on GitOps Applications and ApplicationSets so you can confirm ApplicationSet names, agents, and child Applications during discovery. Go to the [permissions reference](/harness-ai/use-harness-platform/platform-access-control/permissions-reference.md#gitops) to review GitOps permissions.
* **Agent access:** The GitOps agents that run your ApplicationSets must be connected and mapped to the Harness project you are working in.

***

## Understand the migration

Migration changes the **service**. You add `appsetConfigs` with the ApplicationSet names and agents, then remove the Deployment Repository manifests. Release Repository, pipeline YAML, and GitOps Sync normally stay as they are, because Fetch Linked Apps reads this configuration from the service at run time.

Make the change in two stages. Add the references while the Deployment Repository is still in place: Fetch Linked Apps prefers `appsetConfigs` whenever both exist, so the new configuration takes effect immediately and you can compare its results against the old behavior. Remove the Deployment Repository only after that comparison passes.

***

## Compare the service configuration

```yaml
# Before: Deployment Repository
service:
  name: Payments
  identifier: payments
  gitOpsEnabled: true
  serviceDefinition:
    type: Kubernetes
    spec:
      manifests:
        - manifest:
            identifier: deployRepo
            type: DeploymentRepo
            spec:
              store:
                type: Github
                spec:
                  connectorRef: myGithubConnector
                  branch: main
                  paths:
                    - appsets/my-appset.yaml
```

```yaml
# After: Application Set references
service:
  name: Payments
  identifier: payments
  gitOpsEnabled: true
  serviceDefinition:
    type: Kubernetes
    spec:
      appsetConfigs:
        - appset:
            identifier: appSetRef1
            appSetIdentifier: my-application-set   # ApplicationSet metadata.name
            agentIdentifier: my-gitops-agent
```

{% hint style="success" %}
**Use the Application Set name, not the file name**

Fetch Linked Apps historically read the **first** YAML file from the Deployment Repo (`paths[0]` or the first file under `folderPath`) and used `metadata.name`. Always migrate that name. A path like `foo.yaml` that declares `metadata.name: payments` must become `appSetIdentifier: payments`.
{% endhint %}

***

## Choose a migration path

Start with the service YAML, then follow the branch that matches how its Deployment Repository store resolves.

```mermaid
flowchart TD
  start["Service has a Deployment Repository"] --> existing{"Does the service already have Application Set references?"}
  existing -->|Yes| finish["Compare results, then remove the Deployment Repository"]
  existing -->|No| dynamic{"Does the store use an expression or a runtime input?"}
  dynamic -->|No| fixed["Read metadata.name from the file in git, then list every agent that runs it"]
  dynamic -->|Yes| expressions["Choose an expression strategy"]
  fixed --> finish
  expressions --> finish
```

Use these outcomes:

* **Existing references:** Fetch Linked Apps already prefers them, so the Deployment Repository is inactive for that step. Go to [Validate the migration](#validate-the-migration) to compare the results, then remove it.
* **Fixed store:** Resolve one ApplicationSet name from git and find every agent that manages that name. Go to [Identify Application Set names and agents](#identify-application-set-names-and-agents).
* **Dynamic store:** The right answer depends on whether the identity is constant, follows a rule, or is unbounded. Go to [Handle expressions and runtime inputs](#handle-expressions-and-runtime-inputs).
* **Unknown name or agent:** Do not guess. Confirm the live ApplicationSet and its agents before you remove a working Deployment Repository.
* **More than one Deployment Repository:** You can save a service with several, but the legacy Fetch Linked Apps path fails at run time unless exactly one is present, so such a service is already broken. Work out which ApplicationSet the pipeline is meant to use, add references for it, and remove all of the Deployment Repository manifests.

***

## Identify Application Set names and agents

The legacy configuration stores a git location. The replacement stores explicit identities. Work out those identities in this order.

1. **Resolve the git location.**
   * For fixed values, use the configured branch or commit and the first entry in `paths`.
   * For `folderPath`, use the first ApplicationSet YAML file returned from that folder.
   * For expressions or runtime inputs, open a representative pipeline execution and record the resolved connector, repository, branch or commit, and path. The Fetch Linked Apps task log also reports the resolved AppSet name for that run.
2. **Read `metadata.name`.** Open the resolved file and copy the ApplicationSet `metadata.name`. Do not derive the name from the file name.
3. **Find every agent for that name.**
   * Go to **GitOps > Applications** and select **ApplicationSets**. Search for the exact `metadata.name`, then record the agent for every matching row.
   * Also inspect the generated Applications. Confirm that their ApplicationSet owner name matches and record each agent that hosts those children.
   * Do not rely on `harness.io/serviceRef` labels to discover the mapping. Older or custom templates may omit those labels.
4. **Create one pair per agent.** If `payments` has child Applications on `agent-us` and `agent-eu`, add two references with the same `appSetIdentifier` and different `agentIdentifier` values.

### ApplicationSet not found in Harness <a href="#applicationset-not-found-in-harness" id="applicationset-not-found-in-harness"></a>

An ApplicationSet can exist in your Argo CD cluster and generate child Applications even when it does not appear in the Harness **ApplicationSets** list. Harness inventories Applications and ApplicationSets separately, so the child Application records can remain available when the corresponding ApplicationSet inventory row is missing.

An empty **ApplicationSets** list does not block Fetch Linked Apps. The step does not read that list. It queries child Applications by the ApplicationSet name in their Kubernetes owner reference, then filters them by agent. A correct `metadata.name` and agent are sufficient for migration and runtime discovery.

<details>

<summary>ApplicationSet does not appear in the Harness ApplicationSets list</summary>

Harness inventories Applications and ApplicationSets on separate agent paths. The Argo CD ApplicationSet controller creates child Applications with a Kubernetes owner reference to the ApplicationSet, and the agent reports those children to Harness, which stores the owner name as `parentAppRef`. The agent reports ApplicationSets only when ApplicationSet reconciliation is available and enabled on that agent, so child Applications can appear in Harness while the parent ApplicationSet stays absent from the UI.

Check these causes, in order:

1. **The GitOps agent is too old to inventory ApplicationSets.** ApplicationSet reconciliation first shipped in agent v0.90.0. Agents older than v0.90.0 reconcile Applications but do not reconcile ApplicationSets. In that case you see healthy child Applications with an ApplicationSet owner, and zero ApplicationSet rows for that agent. Upgrade the agent to v0.116.0 or later, where ApplicationSet reconciliation is always on.
2. **ApplicationSet reconciliation is disabled on the agent.** Agent versions v0.90.1 through v0.115.x gate ApplicationSet inventory behind `GITOPS_AGENT_APPSET_RECONCILE_ENABLE` (default `false`). When that setting is unset or `false`, the agent continues to report Applications and skips ApplicationSets. Set `GITOPS_AGENT_APPSET_RECONCILE_ENABLE=true` on those versions, then restart or roll the agent so the setting takes effect. In the Harness GitOps agent Helm values, this maps to `harness.configMap.reconcile.appsetReconcile: true`. From agent v0.116.0 the gate is removed and ApplicationSet reconciliation is always on, so the env var is ignored if still present.
3. **The Argo CD project is not mapped to a Harness project.** The agent reports ApplicationSets whose `spec.template.spec.project` maps to a Harness project. An unmapped project prevents a healthy ApplicationSet from appearing.
4. **You are viewing a different project or agent.** ApplicationSets are scoped to the project and the agent that reported them.
5. **The agent is disconnected or has not reconciled recently.** Check the agent status and its last heartbeat after you upgrade or enable reconciliation.
6. **You do not have ApplicationSet view permission.**

To fix: confirm that child Applications exist for the ApplicationSet owner name while **ApplicationSets** lists nothing for the same agent, check the agent version and the agent ConfigMap or Helm values, upgrade the agent to v0.116.0 or later (on v0.90.1 through v0.115.x, set `GITOPS_AGENT_APPSET_RECONCILE_ENABLE=true` instead), then wait for the next successful reconcile and refresh **ApplicationSets**.

</details>

<details>

<summary>Service editor cannot select an ApplicationSet that is missing from the list</summary>

Read the ApplicationSet owner name and the agent from the child Applications and use exactly those values. Fetch Linked Apps queries the Kubernetes owner reference on the child Applications, so it works without a UI row. Configure the reference through YAML, the API, or Terraform instead of the UI selector, then repair the agent inventory (upgrade the agent or enable ApplicationSet reconciliation) so the UI selector works later.

</details>

Then continue based on what does exist:

* **The ApplicationSet was renamed or deleted.** Use the current live name, and reconcile or remove stale child Applications before you compare results.
* **Nothing exists on any agent.** Do not create a speculative reference. Apply or reconcile the ApplicationSet on the intended agent first.
* **Applications have no ApplicationSet owner.** Those applications cannot map to Application Set references. Keep their existing label or explicit GitOps Sync selection, and migrate only the ApplicationSet-owned workflow.

<details>

<summary>Example: one ApplicationSet on two agents</summary>

The first Deployment Repository file contains:

```yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: payments
```

Harness shows child Applications owned by `payments` on `agent-us` and `agent-eu`. Preserve the legacy all-agent behavior with two references:

```yaml
appsetConfigs:
  - appset:
      identifier: payments_us
      appSetIdentifier: payments
      agentIdentifier: agent-us
  - appset:
      identifier: payments_eu
      appSetIdentifier: payments
      agentIdentifier: agent-eu
```

If you omit `agent-eu`, Fetch Linked Apps no longer returns the Applications on that agent.

</details>

<details>

<summary>Example: multiple paths or a folder path</summary>

For this store, only `appsets/payments.yaml` supplied the name at runtime:

```yaml
paths:
  - appsets/payments.yaml
  - appsets/orders.yaml
```

Read `metadata.name` from `appsets/payments.yaml`. Do not create a reference for `orders` unless another supported input or service explicitly selects it.

For `folderPath: appsets/`, inspect the first YAML file returned from that folder. If file ordering is unclear, use a successful Fetch Linked Apps task log to confirm the AppSet name before you migrate.

</details>

***

## Use the matching recipe

### Fixed store and confirmed inventory <a href="#fixed-store-and-confirmed-inventory" id="fixed-store-and-confirmed-inventory"></a>

* **Recognize it:** Connector, repository, branch or commit, and path are fixed values. The ApplicationSet name and all agents are known.
* **Action:** Add one Application Set reference per name and agent pair. Keep any Release Repository unchanged.
* **Test:** Add the references while the Deployment Repository still exists, run Fetch Linked Apps, compare application names and agents, then remove the Deployment Repository.

### Existing Application Set references <a href="#existing-application-set-references" id="existing-application-set-references"></a>

* **Recognize it:** The service contains both `appsetConfigs` and one or more `type: DeploymentRepo` manifests.
* **Action:** Review the existing pairs. Fetch Linked Apps already uses `appsetConfigs`, so the Deployment Repository is inactive for that step.
* **Test:** Confirm the returned applications, then remove every Deployment Repository manifest.

### Git Experience or template-backed service <a href="#git-experience-or-template-backed-service" id="git-experience-or-template-backed-service"></a>

* **Recognize it:** The service has `storeType: REMOTE`, or its service definition comes from a service template.
* **Action:** Change the YAML in the owning git file or template. Do not create an inline copy that will drift from the source.
* **Test:** Commit the git change or publish the template version, then run each service that consumes it.

### Fetch without GitOps Sync <a href="#fetch-without-gitops-sync" id="fetch-without-gitops-sync"></a>

* **Recognize it:** The pipeline runs Update Release Repo, Merge PR, and Fetch Linked Apps, but Argo CD auto-sync performs the cluster rollout.
* **Action:** Check whether a downstream expression reads the Fetch Linked Apps output. If nothing consumes it and the selection is unbounded, go to [Unbounded selection](#unbounded-selection) to remove unused discovery. Otherwise, migrate the service and validate Fetch Linked Apps. The rollout mechanism does not change.

### GitOps Sync without Fetch Linked Apps <a href="#gitops-sync-without-fetch-linked-apps" id="gitops-sync-without-fetch-linked-apps"></a>

* **Recognize it:** GitOps Sync selects applications by name, list, regex, or parent Application without a prior Fetch Linked Apps step.
* **Action:** The Deployment Repository does not supply that Sync selection. Remove it if no Fetch Linked Apps step uses it, and confirm the existing Sync inputs remain unchanged.

***

## Apply the migration

Use the interface that owns your service. Every method writes the same service YAML.

{% tabs %}
{% tab title="UI" %}
Use the UI after you have a confirmed list of AppSet and agent pairs.

1. In the service definition, add an **Application Set reference** for each pair:
   * **App Set:** Exact ApplicationSet `metadata.name`.
   * **Agent:** Agent that owns the matching child Applications.
2. Keep the Deployment Repository in place and save the service. The new references now take precedence in Fetch Linked Apps.
3. Go to [Validate the migration](#validate-the-migration) to compare the results.
4. Once the results match, remove every Deployment Repository manifest. Keep the Release Repository.

If the AppSet does not appear in the selector, do not choose a similar name. Repair or confirm GitOps inventory first. Use the YAML, API, or Terraform method only after you confirm the exact name and agent.
{% endtab %}

{% tab title="YAML" %}
Add `appsetConfigs` next to `manifests`. Keep Release Repository and other manifests unchanged:

```yaml
service:
  name: Payments
  identifier: payments
  gitOpsEnabled: true
  serviceDefinition:
    type: Kubernetes
    spec:
      appsetConfigs:
        - appset:
            identifier: payments_us
            appSetIdentifier: payments
            agentIdentifier: agent-us
        - appset:
            identifier: payments_eu
            appSetIdentifier: payments
            agentIdentifier: agent-eu
      manifests:
        - manifest:
            identifier: releaseRepo
            type: ReleaseRepo
            spec:
              # Keep the existing Release Repository configuration.
```

Apply and validate this intermediate configuration while the Deployment Repository still exists. Once the results match, delete only manifest entries whose `type` is `DeploymentRepo`.

For a remote service, commit this change to the git file that owns the service. For a template-backed service, update and publish the template, then test every consuming service.
{% endtab %}

{% tab title="API" %}
The public Service API reads and replaces the full service YAML. It does not provide a Deployment Repository migration endpoint or a partial PATCH operation.

Set the identifiers and read the current service:

```bash
export HARNESS_BASE="https://app.harness.io"
export HARNESS_API_KEY="pat..."
export ACCOUNT_ID="your_account"
export ORG_ID="your_org"
export PROJECT_ID="your_project"
export SERVICE_ID="payments"

curl --fail --silent --show-error \
  "$HARNESS_BASE/ng/api/v1/orgs/$ORG_ID/projects/$PROJECT_ID/services/$SERVICE_ID" \
  --header "x-api-key: $HARNESS_API_KEY" \
  --header "Harness-Account: $ACCOUNT_ID" \
  > service-response.json

jq --raw-output '.service.yaml' service-response.json > service.yaml
```

Edit `service.yaml`: add the confirmed `appsetConfigs`, preserve all unrelated fields and Release Repository manifests, and keep the Deployment Repository for the first comparison run.

Build the full update request from the GET response:

```bash
jq --rawfile yaml service.yaml \
  '{
    identifier: .service.identifier,
    name: .service.name,
    description: .service.description,
    tags: (.service.tags // {}),
    yaml: $yaml
  }' service-response.json > service-update.json

curl --fail --silent --show-error \
  --request PUT \
  "$HARNESS_BASE/ng/api/v1/orgs/$ORG_ID/projects/$PROJECT_ID/services/$SERVICE_ID" \
  --header "x-api-key: $HARNESS_API_KEY" \
  --header "Harness-Account: $ACCOUNT_ID" \
  --header "Content-Type: application/json" \
  --data @service-update.json
```

Compare the results. Then repeat the GET, edit, and PUT flow to remove `type: DeploymentRepo`. Always start from a fresh GET so that you do not overwrite a concurrent service change.
{% endtab %}

{% tab title="Terraform" %}
The `harness_platform_service` resource stores the complete service definition in its `yaml` argument. There is no `harness_platform_service_v2` resource and no structured `appsetConfigs` block.

Before migration:

```hcl
resource "harness_platform_service" "payments" {
  identifier = "payments"
  name       = "Payments"
  org_id     = "your_org"
  project_id = "your_project"

  yaml = <<-EOT
    service:
      name: Payments
      identifier: payments
      gitOpsEnabled: true
      serviceDefinition:
        type: Kubernetes
        spec:
          manifests:
            - manifest:
                identifier: deployRepo
                type: DeploymentRepo
                spec:
                  store:
                    type: Github
                    spec:
                      connectorRef: account.gitops
                      branch: main
                      paths:
                        - appsets/payments.yaml
            - manifest:
                identifier: releaseRepo
                type: ReleaseRepo
                spec:
                  store:
                    type: Github
                    spec:
                      connectorRef: account.gitops
                      branch: main
                      paths:
                        - environments/prod/config.yaml
  EOT
}
```

Once the results match:

```hcl
resource "harness_platform_service" "payments" {
  identifier = "payments"
  name       = "Payments"
  org_id     = "your_org"
  project_id = "your_project"

  yaml = <<-EOT
    service:
      name: Payments
      identifier: payments
      gitOpsEnabled: true
      serviceDefinition:
        type: Kubernetes
        spec:
          appsetConfigs:
            - appset:
                identifier: payments_us
                appSetIdentifier: payments
                agentIdentifier: agent-us
            - appset:
                identifier: payments_eu
                appSetIdentifier: payments
                agentIdentifier: agent-eu
          manifests:
            - manifest:
                identifier: releaseRepo
                type: ReleaseRepo
                spec:
                  store:
                    type: Github
                    spec:
                      connectorRef: account.gitops
                      branch: main
                      paths:
                        - environments/prod/config.yaml
  EOT
}
```

If Terraform does not manage the service yet, import it at the correct scope before you add the resource configuration:

```bash
terraform import harness_platform_service.payments \
  your_org/your_project/payments
```

Run `terraform plan`, inspect the complete YAML diff, then run `terraform apply`. Validate the intermediate configuration before you remove the Deployment Repository from the heredoc.

Do not use `harness_platform_gitops_applicationset` as a service reference. That resource creates or manages an ApplicationSet CR on an agent; it does not populate `serviceDefinition.spec.appsetConfigs`.
{% endtab %}
{% endtabs %}

***

## Handle expressions and runtime inputs

Treat the store as dynamic when the outer `store`, connector, repository, branch, commit, path, or folder path contains `<+...>` or a runtime input. A default value does not make a runtime input fixed.

{% hint style="warning" %}
**Every reference applies to every run**

With a Deployment Repository, the path could be an expression such as `<+env.identifier>/appset.yaml`. Each pipeline run resolved that expression once, using that run's variables, and fetched exactly one file, so the run itself chose the ApplicationSet:

* A run deploying to `dev` resolved the path to `dev/appset.yaml`, which defined `payments-dev`.
* A run deploying to `prod` resolved the same path to `prod/appset.yaml`, which defined `payments-prod`.

Application Set references do not work that way. Fetch Linked Apps queries **every** reference on the service and returns the combined result. If you migrate by listing every identity the path could ever resolve to (the superset of all resolutions), then:

* Both `payments-dev` and `payments-prod` apply to every run, whichever environment you deploy to.
* A `dev` run also fetches the `prod` applications, and a `prod` run also fetches the `dev` applications.
* A downstream GitOps Sync step would sync applications for environments the run did not target.

To preserve the old per-run behavior, put the expression on the reference itself instead of listing the superset. Both `appSetIdentifier` and `agentIdentifier` accept Harness expressions and resolve per execution, so a single reference resolves to exactly one ApplicationSet and agent in each run, the same resolution the dynamic path used to perform:

```yaml
appsetConfigs:
  - appset:
      identifier: payments_by_environment
      appSetIdentifier: payments-<+env.identifier>  # dev run -> payments-dev, prod run -> payments-prod
      agentIdentifier: <+env.variables.gitopsAgent>
```

When the identity follows no such rule, enumerate the pairs and set `filterByServiceEnvCluster: true` on the Fetch Linked Apps step so each run keeps only the applications that match its own service, environment, and cluster. Go to [Variable application set identity](#variable-application-set-identity).
{% endhint %}

### Fixed application set identity <a href="#fixed-application-set-identity" id="fixed-application-set-identity"></a>

A path can vary while the ApplicationSet identity stays the same. Test every supported input value. If each resolved file has `metadata.name: payments` and runs on `agent-prod`, use one fixed reference:

```yaml
appsetConfigs:
  - appset:
      identifier: payments_prod
      appSetIdentifier: payments
      agentIdentifier: agent-prod
```

### Expression-based identity <a href="#expression-based-identity" id="expression-based-identity"></a>

This is the closest replacement for a dynamic Deployment Repository. Both `appSetIdentifier` and `agentIdentifier` accept Harness expressions and resolve per execution, so one reference resolves to exactly one ApplicationSet and agent in each run, just as the old path resolved to one file.

```yaml
# Legacy path: <+env.identifier>/appset.yaml
#   dev/appset.yaml  -> metadata.name: payments-dev  on agent-dev
#   prod/appset.yaml -> metadata.name: payments-prod on agent-prod

appsetConfigs:
  - appset:
      identifier: payments_by_environment
      appSetIdentifier: payments-<+env.identifier>
      agentIdentifier: <+env.variables.gitopsAgent>
```

Use this only when the naming rule holds for every environment and input the service supports. Verify the rule against each one: if an expression resolves to a name that does not exist, the reference returns no applications, and the step reports fewer applications instead of failing.

Configure expression-based references through YAML, the API, or Terraform. The service editor App Set field accepts only fixed values from the ApplicationSets list, and the Agent field accepts fixed values or runtime inputs.

### Variable application set identity <a href="#variable-application-set-identity" id="variable-application-set-identity"></a>

When the names follow no single rule, list every known pair:

```yaml
appsetConfigs:
  - appset:
      identifier: payments_dev
      appSetIdentifier: payments-dev
      agentIdentifier: agent-dev
  - appset:
      identifier: legacy_orders
      appSetIdentifier: orders-legacy
      agentIdentifier: agent-prod
```

Because both references apply to every run, set `filterByServiceEnvCluster: true` on the Fetch Linked Apps step so that the step keeps only the applications that match the current service, environment, and cluster.

Use this option only when every generated Application has the correct Harness service, environment, and cluster references. The filter drops an Application when it cannot match those references. If your ApplicationSet templates do not add that metadata, do not enumerate the pairs. Use one expression-based reference per run or split the service instead.

### Unbounded selection <a href="#unbounded-selection" id="unbounded-selection"></a>

There is no lossless direct mapping when a runtime input can select arbitrary connectors, repositories, entire stores, model names, namespaces, or unknown future files. A floating branch or commit creates the same problem when the selected file can change its `metadata.name`.

Use this order to decide what to change:

1. **Check whether Fetch Linked Apps has a consumer.** Look for a later GitOps Sync step or an expression that reads the Fetch Linked Apps output. If neither exists and Merge PR plus Argo CD auto-sync performs the rollout, remove Fetch Linked Apps and the Deployment Repository together. You do not need `appsetConfigs` only to preserve unused discovery.
2. **Pass identity instead of location.** Replace arbitrary repository and path inputs with explicit `appSetName` and `gitOpsAgent` inputs on the pipeline or template. Restrict those inputs through approved input sets, triggers, or allowed values. Use them directly in `appSetIdentifier` and `agentIdentifier`.
3. **Use a verified naming rule.** If the exact ApplicationSet name and agent can be computed from existing inputs for every run, use one expression-based reference. Test every supported environment, region, and agent.
4. **Split the service.** Create a service for each deployment domain when one service can select unrelated repositories, models, ApplicationSets, or agents and no single verified rule produces the pair.
5. **Keep the Deployment Repository until the contract changes.** Do not replace it with a snapshot of recent pairs when callers can introduce new values.

{% hint style="warning" %}
**No lossless snapshot**

A list of pairs observed in recent runs does not cover values that have never run. Do not remove the Deployment Repository until the supported input domain is explicit and every value has a replacement.
{% endhint %}

<details>

<summary>Worked example: model and environment select the AppSet file</summary>

Consider a path with pipeline variables:

```
<+pipeline.variables.gitEnvironment>/gitops/<+pipeline.variables.namespace>/<+pipeline.variables.modelName>-deployment-appset.yaml
```

This pattern can reuse one service across model names, environments, namespaces, regions, and agents. Recent runs can show many parent ApplicationSet names across many agents even when the service has no `serviceRef` labels and the ApplicationSet page has no matching rows.

Do not copy those observed pairs into one static list. Every pair would apply to every run, and service filtering cannot recover the intended subset when the generated Applications lack Harness service and environment references.

Apply the first option that fits:

1. **Remove unused discovery:** If no GitOps Sync step or downstream expression consumes the Fetch Linked Apps output, remove Fetch Linked Apps and the Deployment Repository. Keep Update Release Repo and Merge PR unchanged so Argo CD auto-sync continues the rollout.
2. **Make the pair an input:** Add explicit ApplicationSet name and agent inputs to the shared pipeline template. Populate them from approved input sets or triggers for each model, environment, and region. Reference those values from `appSetIdentifier` and `agentIdentifier`.
3. **Express a complete rule:** Use expressions only when the rule produces the exact ApplicationSet `metadata.name` and agent for every supported combination. A rule based only on `modelName` is incomplete when the real names also vary by region or agent.
4. **Split the service:** Create separate services when callers can introduce arbitrary models or repositories, or when no stable expression maps the inputs to one pair.

Before you remove the Deployment Repository, run one case for each supported environment and agent. Compare the Fetch Linked Apps output with the legacy result. Also test a value that callers must no longer use and confirm that the new input contract rejects it.

</details>

***

## Validate the migration

The Fetch Linked Apps step lists the applications it discovered in its step output. That list is what you compare. Run the same pipeline with the same inputs before and after the change, and check that the same applications appear.

1. **Record a baseline.** With the Deployment Repository still active, run the pipeline and open the Fetch Linked Apps step output. Note every application name and its agent. Repeat for each environment or runtime input the service supports.
2. **Add the references.** Save the service with both `appsetConfigs` and the Deployment Repository present. Fetch Linked Apps now uses the references.
3. **Run the same inputs again and compare.** The application and agent pairs in the step output should match the baseline exactly. More applications than the baseline usually means references for another environment are being included, so revisit [Handle expressions and runtime inputs](#handle-expressions-and-runtime-inputs). Fewer usually means a missing agent or an ApplicationSet name that does not exist.
4. **Remove the Deployment Repository.** Delete every manifest whose `type` is `DeploymentRepo`, keep the Release Repository, and run the pipeline once more.

Before you call the migration complete, confirm that the step output matches the baseline for every supported input, that Sync or Argo CD auto-sync behavior is unchanged, and that no `DeploymentRepo` manifest remains on the service.

***

## Understand behavior after migration

* **No delegate for discovery:** Fetch Linked Apps no longer runs a delegate task. It resolves applications from the service configuration, so its delegate selectors no longer affect discovery. Other steps, such as Update Release Repo and Merge PR, still need a delegate.
* **Explicit agent scope:** The legacy path returned children for the resolved ApplicationSet name across all agents. The new path returns children only for the agents named in `appsetConfigs`.
* **Every reference applies to every run:** The step combines the results of all references on the service. Select **Filter applications per configured service/env** when you need a per-run subset limited to the pipeline service, environment, and cluster.
* **No location-only lookup:** A reference must provide the ApplicationSet name, or an expression that produces it. It cannot discover a name from a git file or folder location alone.

***

## Deployment Repository removal

After **October 4, 2026**, the Deployment Repository manifest is removed, and pipelines that still depend on it fail loudly instead of changing behavior silently:

* **Fetch Linked Apps fails with an explicit error.** A run whose service still relies on a Deployment Repository fails the step with an error that directs you to migrate to Application Set references, and the pipeline stops at that step unless you have configured a custom failure strategy.
* **No silent fallback.** The step does not degrade to a partial or different set of applications. A pipeline that used to sync a group of ApplicationSet-generated applications does not start syncing a smaller set or a single application; it fails until you migrate the service.
* **Other steps do not read the Deployment Repository.** Update Release Repo, Merge PR, and GitOps Sync are unaffected by the removal itself, but a failed Fetch Linked Apps step still prevents later steps in the stage from running under the default failure strategy.

Migrate each service before the deprecation date so that your pipelines keep working without interruption.

***

## Next steps

* Go to [GitOps services](/continuous-delivery/use-gitops/gitops-entities/service.md) to review service configuration.
* Go to [GitOps PR pipelines](/continuous-delivery/use-gitops/pr-pipelines/pr-pipelines-basics.md) to review Fetch Linked Apps and Sync behavior.
* Go to [ApplicationSet basics](/continuous-delivery/use-gitops/applicationsets/appset-basics.md) to review ApplicationSet concepts.

{% @harness-feedback/feedback %}
