# Manage Resources with GitOps

> For the complete documentation index, see [llms.txt](https://docs.redpanda.com/llms.txt). Component-specific: [agentic-data-plane-full.txt](https://docs.redpanda.com/agentic-data-plane-full.txt)

---
title: Manage Resources with GitOps
latest-operator-version: v26.2.1
latest-console-tag: v3.10.0
latest-connect-version: 4.104.0
latest-redpanda-tag: v26.2.1
docname: gitops
page-component-name: agentic-data-plane
page-version: master
page-component-version: master
page-component-title: Agentic Data Plane
page-relative-src-path: gitops.adoc
page-edit-url: https://github.com/redpanda-data/adp-docs/edit/main/modules/cli/pages/gitops.adoc
description: "Keep Agentic Data Plane resources in Git and reconcile them with rpk ai apply and diff: declarative create-or-update, drift detection, and CI gating."
page-topic-type: how-to
personas: platform_engineer, agent_builder
learning-objective-1: Export an Agentic Data Plane resource to a YAML manifest you can commit to Git
learning-objective-2: Reconcile resources with apply, and preview changes with diff
learning-objective-3: Gate continuous integration on configuration drift
page-git-created-date: "2026-06-26"
page-git-modified-date: "2026-07-30"
---

<!-- Source: https://docs.redpanda.com/agentic-data-plane/cli/gitops.md -->

GitOps keeps the definition of your [Agentic Data Plane](https://docs.redpanda.com/agentic-data-plane/reference/glossary/#agentic-data-plane) resources in Git and reconciles the live environment toward those files, instead of running imperative `create`, `update`, and `delete` commands by hand. With the [Agentic Data Plane CLI](../), `rpk ai <resource> apply -f` creates what is missing and updates what has drifted, and `rpk ai <resource> diff -f` is a read-only dry-run that reports what `apply` would change. In these commands, `<resource>` is one of the resource command groups that support GitOps:

-   LLM providers (`rpk ai llm-provider`)

-   MCP servers (`rpk ai mcp-server`)

-   OAuth providers (`rpk ai oauth`)

-   OAuth clients (`rpk ai oauth-client`)

-   Agents (`rpk ai agent`)


After reading this page, you will be able to:

-   Export an Agentic Data Plane resource to a YAML manifest you can commit to Git

-   Reconcile resources with apply, and preview changes with diff

-   Gate continuous integration on configuration drift


## [](#prerequisites)Prerequisites

-   The [Agentic Data Plane CLI installed and connected](../) to an Agentic Data Plane environment.

-   Permission to create and update the resources you manage. The reconcile commands call the same APIs as `create` and `update`. See [Roles and Permissions Reference](https://docs.redpanda.com/agentic-data-plane/control/permissions-reference/).

-   Any secrets your manifests reference already created in Agentic Data Plane. Manifests name secrets by reference, for example, `OPENAI_API_KEY`; the CLI does not create secrets.


## [](#how-apply-and-diff-reconcile)How apply and diff reconcile

A manifest is plain resource YAML: the same shape a `get -o yaml` dump produces. The CLI compares each manifest against the live resource of the same name and resolves one of three outcomes:

Create

No resource of that name exists, so `apply` creates it.

Update

The resource exists and a field in the manifest differs from the live value, so `apply` updates the differing fields.

Unchanged

The resource exists and every field the manifest names already matches.

The reconcile rules are deliberate, and they are not the same as a full-object replace:

Presence drives updates

A field that is present in the manifest and differs from the live resource is updated. A field you omit is left untouched. To clear a field, write it explicitly with an empty or zero value.

Collections replace wholesale

Lists, maps, and provider or backend variants are replaced as a unit, not merged element by element.

Create-only fields are immutable

A field that can be set only at creation time, such as an LLM provider’s `type`, an MCP server’s backend kind, an OAuth provider’s `client_id`, or an agent’s managed-or-self-managed kind, cannot change on an existing resource. Changing one is an error that tells you to delete and recreate the resource.

Request-only fields are instructions, not state

A few fields tell the gateway to do something rather than describe what a resource is. An MCP server’s `remote.user_oauth.automatic_setup` asks the gateway to set up OAuth for the server itself instead of attaching a provider by name. Set it in a manifest when you want automatic setup, and leave `provider_name` out: on an existing server an empty `provider_name` preserves the provider already attached, and setting both is an error. Omitting `automatic_setup` leaves an existing attachment untouched, so an unrelated edit such as a scopes change never re-runs OAuth setup. See [User-delegated OAuth](https://docs.redpanda.com/agentic-data-plane/connect/user-delegated-oauth/).

Secrets stay by reference

Manifests reference secrets by name, for example, `api_key_ref` and `client_secret_ref`, and never contain secret values, so a manifest is safe to commit to Git.

The `apply` command does not delete resources that are absent from your manifests; there is no prune. The `diff` command checks only the fields a manifest names, so it does not detect a resource that exists in the environment but is missing from your manifests, nor drift in a field a manifest omits.

## [](#export-a-resource-to-a-manifest)Export a resource to a manifest

Start from a live resource so the manifest is complete. Dump it to YAML and redirect it to a file:

```bash
rpk ai llm-provider get openai -o yaml > openai.yaml
```

A dumped OpenAI provider looks like this, ready to commit:

```yaml
'@type': type.googleapis.com/redpanda.api.adp.v1alpha1.LLMProvider
created_at: "2026-06-20T10:15:30Z"
display_name: OpenAI
enabled: true
name: openai
openai_config:
  api_key_ref: OPENAI_API_KEY
provider_models:
- name: gpt-4o
- name: gpt-4o-mini
type: LLM_PROVIDER_TYPE_OPENAI
updated_at: "2026-06-20T10:15:30Z"
url: https://openai.aigw.d0example1cluster234.clusters.rdpa.co/openai/v1
```

The `@type` line records the resource kind. It is optional when you apply with a resource command, because `rpk ai llm-provider apply` already implies the kind, but keeping it lets a reader and any validator know what the file describes.

> 📝 **NOTE**
>
> The `created_at`, `updated_at`, and `url` fields are server-managed and read-only. The `apply` command ignores them, so you can leave them in the file or strip them. The `api_key_ref` value is a reference to a secret, not the key itself.

## [](#preview-changes-with-diff)Preview changes with diff

Edit the manifest, then preview the effect before you touch the environment. For example, change the display name:

```yaml
display_name: OpenAI (production)
```

Run `diff` to see the plan:

```bash
rpk ai llm-provider diff -f openai.yaml
```

```text
~ openai (update: display_name)
```

The `diff` command marks each manifest with one of three symbols and changes nothing:

| Symbol | Meaning |
| --- | --- |
| + | The apply command would create the resource. |
| ~ | The apply command would update the resource. The changed fields follow in parentheses. |
| = | The resource already matches; apply would leave it unchanged. |

The `diff` command exits with a non-zero status when any change is pending, and zero when the environment already matches every manifest. That exit code is what lets continuous integration gate on drift.

## [](#apply-changes)Apply changes

Reconcile the environment toward the manifest:

```bash
rpk ai llm-provider apply -f openai.yaml
```

The `apply` command prints one line per manifest as it works:

```text
updated openai (display_name)
```

A first-time apply of a resource that does not yet exist prints `created openai` instead, and a manifest that already matches the environment prints `unchanged openai`. The CLI plans every manifest before it changes anything, so a malformed manifest aborts the run before any write. If a later write fails, the lines already printed tell you exactly what was applied.

## [](#apply-many-manifests-at-once)Apply many manifests at once

The `-f` flag is repeatable and accepts a file, a directory, or a stream:

```bash
# A directory of manifests; the CLI reads every .yaml and .yml file, sorted by name.
rpk ai mcp-server apply -f ./manifests/

# Several paths in one run.
rpk ai llm-provider apply -f openai.yaml -f anthropic.yaml

# Standard input, for piping a manifest from another tool.
rpk ai oauth apply -f -
```

A single file can hold more than one manifest. Separate documents with a line containing only `---`.

## [](#gate-continuous-integration-on-drift)Gate continuous integration on drift

Because `diff` exits non-zero when the environment differs from your manifests, a continuous-integration job can fail the build whenever the live environment has drifted from Git:

```bash
# Fails the job if an apply would change anything.
rpk ai llm-provider diff -f ./llm/
rpk ai mcp-server diff -f ./mcp/
```

A common pipeline runs `diff` on a pull request to preview changes, then runs `apply` after the merge to roll them out:

```bash
# Deploy step, after merge to the main branch.
rpk ai llm-provider apply -f ./llm/
rpk ai mcp-server apply -f ./mcp/
```

## [](#manifests-are-declarative)Manifests are declarative

A manifest describes the full intended state, so an omitted field means zero, not the convenience default that `create` fills in. A manifest that omits `enabled` creates a disabled resource, and `diff` then reports no drift, because the disabled state matches the manifest.

To avoid surprises:

-   Start from a `get -o yaml` dump, which is already complete, rather than hand-writing a manifest from scratch.

-   Set `enabled: true` explicitly when you want an active resource.


One subtlety follows from this: a dump of an already-disabled resource omits `enabled`, because `false` is the field’s zero value and the dump omits zero values. Reapplying that dump elsewhere also produces a disabled resource.

## [](#troubleshooting)Troubleshooting

| Symptom | Resolution |
| --- | --- |
| The apply or diff command reports that a field is immutable and tells you to delete and recreate the resource. | You changed a create-only field, such as an LLM provider’s type or an OAuth provider’s client_id. Restore the original value, or delete the resource and recreate it from the new manifest. |
| The apply or diff command fails to decode the manifest and mentions unknown or misspelled fields. | A key in the manifest is not a field of the resource. The CLI rejects unknown keys rather than dropping them silently. Fix the key. Start from a get -o yaml dump to get the exact field names. |
| The diff command reports no drift, but you know the environment has extra resources. | The diff and apply commands never prune. They act only on the resources your manifests name. Delete unwanted resources with rpk ai <resource> delete. |
| A field you changed in the environment keeps coming back after apply. | The apply command overwrites a field only when the manifest names it and the value differs. If the change is not in your manifest, add it, then reapply. |

## [](#next-steps)Next steps

-   [Use the Agentic Data Plane CLI](../)

-   [Configure an LLM Provider](https://docs.redpanda.com/agentic-data-plane/gateway/configure-provider/)

-   [Create an MCP Server](https://docs.redpanda.com/agentic-data-plane/connect/create-server/)