# Use Diagrid with your existing Dapr setup

Diagrid is built on Dapr OSS, so it supports the same APIs, resource types, and a selection of Dapr components\*. As a result, your existing Dapr resources, such as policies and configurations, work without modification, and your application code can keep using the same Dapr SDKs.

This guide walks you through the process of migrating your workloads from Dapr OSS on Kubernetes to Diagrid.

To try the whole migration on a local cluster before you plan your own, follow the [migration quickstart](https://docs.diagrid.io/dapr-open-source/migrate-to-diagrid/quickstart).

:::warning Check compatibility before you migrate

- **Components:** \*Diagrid supports a curated subset of Dapr OSS components — state stores, pub/sub brokers, secret stores, bindings, conversation, and so on. Check the [Diagrid component specs reference](https://docs.diagrid.io/references/diagrid/components-reference/intro) and confirm that every component type you rely on is supported. Components that aren't listed won't work on Diagrid and need an alternative or an exception before you can complete the migration.
- **Secret stores:** Diagrid does not support `secretstores.kubernetes`, the secret store most Dapr applications on Kubernetes use. See [Secret stores](#secret-stores) for what to do instead — depending on how you use it, you may need no secret store at all, or you may need to provision one before you migrate.
- **Configuration:** In Diagrid you can only configure some fields within a Dapr `Configuration` resource; others are reserved for Diagrid's internal use. See [Dapr configuration compatibility](#dapr-configuration-compatibility) for the full list of editable and reserved fields.
  :::

## Prerequisites

- A Kubernetes cluster running Dapr open source.
- A Diagrid organization you have global `admin` or `editor` access to. You can sign up for free at [Diagrid](https://catalyst.diagrid.io).
- The [Diagrid CLI](https://docs.diagrid.io/getting-started/local-installation#diagrid-cli) installed and authenticated.
- A secret store Diagrid supports — AWS Secrets Manager, Azure Key Vault or HashiCorp Vault — but only if one of your applications calls the Dapr Secrets API. See [Secret stores](#secret-stores).
- [cert-manager](https://cert-manager.io/) in the cluster — required only if you migrate with the [Diagrid Kubernetes Operator](https://docs.diagrid.io/dapr-open-source/migrate-to-diagrid/kubernetes-operator), whose mutating webhook needs it to issue a serving certificate. Install it before the operator:

  ```bash
  kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml
  kubectl wait --for=condition=Available -n cert-manager deploy --all --timeout=120s
  ```
- Every service your components connect to must be reachable from Diagrid — if you run the Dapr server remotely, which is the common choice. See [Reachable backing services](#reachable-backing-services).

### Reachable backing services

With a remote Dapr server, the server runs in Diagrid's region. It cannot resolve an address that only exists inside your cluster, so a component pointing at `redis.demo.svc.cluster.local:6379` will not work once migrated.

This is not an edge case. If your Redis, Kafka or Postgres runs in the same cluster as your applications — which is common — every component is affected at once.

Migration tooling cannot fix this for you: the service is yours, and the change is to your infrastructure. Before you migrate, either expose each backing service on an address Diagrid can reach, or move it to one that already is. Then update the component's metadata to that address.

```yaml
spec:
  type: state.redis
  metadata:
    - name: redisHost
      value: my-redis.example.com:6379   # reachable from Diagrid
```

Two things to watch:

- Check the credentials the new endpoint expects. A managed Redis that authenticates with a username needs `redisUsername` set as well as `redisPassword`; Dapr does not infer it from the connection string.
- Repointing a state store does not move the data in it. If the component holds actor state you still need, migrate that data yourself.

### Check before you start

Two commands answer different questions, and both are worth running:

```bash
# What would migrate, and what has to be fixed first.
# Reads the cluster only: no login, no operator, no Diagrid project needed.
diagrid k8s-operator compatibility <namespace>

# Exactly what would be copied, and under which App IDs.
# Needs the operator installed and a Diagrid project.
diagrid k8s-operator migrate <namespace> --dry-run
```

Run the first while you are still deciding whether to migrate at all — it reports unsupported component types, component settings Diagrid rejects, addresses it cannot reach, the `Configuration` fields that will not survive, and what the cluster itself still needs.

Run the second immediately before the real migration. Note what it can and cannot tell you: migrating in place, it lists every workload and resource it would change; with `--new-namespace` it lists what it would copy, but reports no workloads migrated, because the target namespace does not exist yet and there is nothing there for it to annotate.

## Starting point: Dapr OSS on Kubernetes

A typical Dapr OSS setup looks like this:

- The Dapr control plane installed in the `dapr-system` namespace via Helm or the Dapr CLI.
- Application pods annotated with `dapr.io/enabled: "true"`, which get a Dapr sidecar injected by the Dapr operator.
- Dapr CRDs such as Components, Configurations, Subscriptions, and Resiliency policies, stored in the Kubernetes cluster.

## Two decisions shape your migration

Before you start, you need to make two decisions — one architectural, one operational:

1. [**Where the Dapr server runs**](#dapr-server-location-remote-or-local) — in Diagrid (remote) or in your application Kubernetes cluster (local).
2. [**Which tools you want to use**](#tooling-diagrid-native-or-kubernetes-native) — with Diagrid-native tooling (UI, CLI, Terraform) or with the Diagrid Kubernetes Operator.

### Dapr Server location: remote or local

- **Remote** — Diagrid's Dapr server runs in Diagrid. Your app calls the Dapr API via Diagrid's endpoint and no Dapr server runs inside your app pod.
- **Local** — Diagrid's Dapr server runs as a sidecar in your app pod, configured to talk to Diagrid's Dapr control plane. Your app continues to talk to the Dapr APIs over `localhost` the same as Dapr OSS.

Pick **remote** for zero server ops and the lowest infrastructure footprint. Pick **local** when the Dapr server must stay inside your network boundary or be co-located with the workload.

In a public region, a local Dapr server gets none of the Diagrid-managed services. Their credentials reach the region's own database and message broker, so Diagrid does not send them to a sidecar that runs on your compute. That means:

- Workflows and agent state are unavailable to that App ID.
- The managed KV store and managed pub/sub are not loaded into its sidecar.
- State store and pub/sub components you bring yourself still load as usual.

If the app needs workflows or the managed services in a public region, choose **remote**. Dedicated Cloud, BYOC, and Enterprise Server regions serve a single organization, so a local Dapr server there keeps all of them.

**Diagram: Remote location: the Dapr server runs in Diagrid.** Your app calls the Dapr API on Diagrid's endpoint. No Dapr server runs in the app pod.

- **App pod**: App
- **Diagrid**: Dapr server, Dapr control plane

- App → Dapr server (remote)
- Dapr server → Dapr control plane

**Diagram: Local location: the Dapr server runs in your cluster.** The Dapr server runs as a sidecar in the app pod and connects to Diagrid's Dapr control plane. Your app keeps calling it on localhost.

- **App pod**: App, Dapr server
- **Diagrid**: Dapr control plane

- App → Dapr server (localhost)
- Dapr server → Dapr control plane

#### Local Dapr Server networking

If you choose the `local` location for your Dapr Server, note that it will run inside your application Kubernetes cluster and is thus subject to your cluster's networking rules and policies.
All traffic between the app, the Dapr Server sidecar, and any infrastructure is fully within your control and cannot be influenced by Diagrid. You are responsible for configuring appropriate network policies to isolate your workloads and allow access to the services and infrastructure they need.

### Tooling: Diagrid-native or Kubernetes-native

You can choose the tooling that best suits your setup for migrating and managing your workloads on Diagrid. For existing Dapr apps running on Kubernetes, the Diagrid Kubernetes Operator provides a simpler migration path. Once you've migrated your workloads, you can switch to using the native Diagrid tooling if you prefer.

Select from one of the options below to see how you can use that tooling to migrate your workloads.

| Path | Best when | What it does |
| --- | --- | --- |
| [Diagrid-native](https://docs.diagrid.io/dapr-open-source/migrate-to-diagrid/diagrid-native) | Diagrid is your source of truth and you drive infra from the UI, CLI, or Terraform | You create Diagrid resources yourself, then wire each workload to Diagrid |
| [Kubernetes-native](https://docs.diagrid.io/dapr-open-source/migrate-to-diagrid/kubernetes-operator) | Your platform team already manages Dapr declaratively in-cluster | The Diagrid Kubernetes Operator reconciles Dapr's K8s native resources into Diagrid |

## Secret stores

Diagrid supports three secret store types: `aws.secretsmanager`, `azure.keyvault` and `hashicorp.vault`. It does not support `secretstores.kubernetes`, the default secret store for a Dapr application on Kubernetes. Applying a component of that type is rejected:

```
invalid value of 'secretstores.kubernetes' provided for 'spec.type' field:
please use a supported type. See our docs @ https://docs.diagrid.io.
```

What to do about it depends on how your applications use the store, and the two cases are independent. Check both.

### Components that reference a secret

A component whose metadata uses `secretKeyRef` needs no secret store on Diagrid. Supply the value as plaintext instead:

```yaml
# Dapr OSS
- name: redisPassword
  secretKeyRef:
    name: redis-secret
    key: password

# Diagrid
- name: redisPassword
  value: "<password>"
```

Diagrid identifies sensitive fields from the Dapr component metadata schema and extracts them into a Diagrid-managed secret store before the resource is persisted, so the plaintext value is never stored in the control plane. See [Managing secrets](https://docs.diagrid.io/operate/project-operations/secrets).

This is a change of secrets model rather than a component swap: the value moves out of your cluster and into Diagrid.

:::note
That store resolves component configuration. It is not a store your application can read through the Secrets API, so extraction does not cover the case below.
:::

### Applications that call the Secrets API

An application that calls `GetSecret` or `GetBulkSecret` needs a real secret store, so `secretstores.kubernetes` has to be replaced with one of the three supported types. Provision it **before** you migrate, because the application fails at runtime without it.

Two things to plan for:

- **Move the secret data.** The contents of your Kubernetes `Secret` objects have to exist in the new store before the migrated application reads them.
- **Keep the component name.** Your application names the store in every call, so reusing the original component name — only changing `spec.type` and its metadata — means the application code does not change.

## Dapr configuration compatibility

Diagrid supports the Dapr [`Configuration`](https://docs.dapr.io/operations/configuration/configuration-overview/) resource, but some fields are reserved for Diagrid's internal use and cannot be changed. Behavior can also differ between **public** (multi-tenant Diagrid Cloud) regions and **single-tenant** regions (Dedicated Cloud, BYOC, and Enterprise Server). Each field falls into one of these categories per region:

- **🔒 Locked** — Diagrid manages this field. Any value you supply is ignored or overwritten.
- **✏️ Editable** — You can set this field, subject to the validation noted.

| Field | Public regions | Single-tenant regions | Notes |
| --- | --- | --- | --- |
| `accessControl.trustDomain` | 🔒 Locked | 🔒 Locked | Reserved by Diagrid; set automatically to your organization's SPIFFE trust domain. |
| `accessControl.policies[].trustDomain` | 🔒 Locked | 🔒 Locked | Forced to your organization's trust domain. |
| `accessControl.policies[].namespace` | 🔒 Locked | 🔒 Locked | Forced to your project namespace (`prj-<namespace>`). |
| `features` | 🔒 Locked | 🔒 Locked | Preview feature toggles are managed by Diagrid. |
| `api.allowed` | 🔒 Locked | 🔒 Locked | Restricted to the Diagrid platform API allow-list. |
| `metrics` | 🔒 Locked | 🔒 Locked | Diagrid applies its own metric cardinality rules. |
| `tracing` (`otel` / `zipkin`) | ✏️ Editable | ✏️ Editable | Requires `otel.isSecure`. Custom/internal endpoints are rejected in public regions. |
| `workflow.maxConcurrentWorkflowInvocations`, `workflow.maxConcurrentActivityInvocations` | 🔒 Locked | ✏️ Editable | Per-sidecar concurrency limits. Discarded in public regions. In single-tenant regions you can lower them, but not past Diagrid's ceilings of 5000 workflows and 10000 activities, which also apply when you omit them. |
| `workflow.workflowConcurrencyLimits`, `workflow.activityConcurrencyLimits` | ✏️ Editable | ✏️ Editable | Per-name concurrency limits, enforced across every replica. The way to bound concurrency in a public region. |
| `workflow.globalMaxConcurrentWorkflowInvocations`, `workflow.globalMaxConcurrentActivityInvocations` | 🔒 Locked | 🔒 Locked | Accepted and stored, but not passed to the workflow engine, so setting either changes nothing in any region today. |
| `workflow.stateRetentionPolicy` (`anyTerminal`, `completed`, `failed`, `terminated`) | ✏️ Editable | ✏️ Editable | Per-terminal-state retention durations (Go duration strings). Added in Dapr 1.17. |
| `httpPipeline` / `appHttpPipeline` handlers (user middleware) | ✏️ Editable | ✏️ Editable | User handlers are appended after Diagrid's reserved base handler, which is always present and cannot be removed. |
| `accessControl.defaultAction` + `accessControl.policies[]` (`appId`, `defaultAction`, `operations` with `name` / `httpVerb` / `action`) | ✏️ Editable | ✏️ Editable | Validated as allow/deny rules. |

## Next steps

1. Try the migration end to end in the [migration quickstart](https://docs.diagrid.io/dapr-open-source/migrate-to-diagrid/quickstart).
2. Migrate your workloads with the [Diagrid Kubernetes Operator](https://docs.diagrid.io/dapr-open-source/migrate-to-diagrid/kubernetes-operator) or [Diagrid-native tooling](https://docs.diagrid.io/dapr-open-source/migrate-to-diagrid/diagrid-native).
3. [Cut over and decommission Dapr OSS](https://docs.diagrid.io/dapr-open-source/migrate-to-diagrid/cut-over) — move traffic onto Diagrid and remove the Dapr OSS control plane.
