Upgrade Dapr on a Kubernetes cluster
You can upgrade the Dapr control plane on a Kubernetes cluster using either the Dapr CLI or Helm.
Note
Refer to the Dapr version policy for guidance on Dapr’s upgrade path.Upgrade using the Dapr CLI
You can upgrade Dapr using the Dapr CLI.
Prerequisites
Upgrade existing cluster to 1.19.0
dapr upgrade -k --runtime-version=1.19.0
You can provide all the available Helm chart configurations using the Dapr CLI.
Troubleshoot upgrading via the CLI
There is a known issue running upgrades on clusters that may have previously had a version prior to 1.0.0-rc.2 installed on a cluster.
While this issue is uncommon, a few upgrade path edge cases may leave an incompatible CustomResourceDefinition installed on your cluster. If this is your scenario, you may see an error message like the following:
❌ Failed to upgrade Dapr: Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
The CustomResourceDefinition "configurations.dapr.io" is invalid: spec.preserveUnknownFields: Invalid value: true: must be false in order to use defaults in the schema
Solution
Run the following command to upgrade the
CustomResourceDefinitionto a compatible version:kubectl replace -f https://raw.githubusercontent.com/dapr/dapr/release-1.19/charts/dapr/crds/configuration.yamlProceed with the
dapr upgrade --runtime-version 1.19.0 -kcommand.
Upgrade using Helm
You can upgrade Dapr using a Helm v3 chart.
❗Important: The latest Dapr Helm chart no longer supports Helm v2. Migrate from Helm v2 to Helm v3.
Prerequisites
Upgrade existing cluster to 1.19.0
As of version 1.0.0 onwards, existing certificate values will automatically be reused when upgrading Dapr using Helm.
Note Helm does not handle upgrading resources, so you need to perform that manually. Resources are backward-compatible and should only be installed forward.
Upgrade Dapr to version 1.19.0:
kubectl replace -f https://raw.githubusercontent.com/dapr/dapr/v1.19.0/charts/dapr/crds/components.yaml kubectl replace -f https://raw.githubusercontent.com/dapr/dapr/v1.19.0/charts/dapr/crds/configuration.yaml kubectl replace -f https://raw.githubusercontent.com/dapr/dapr/v1.19.0/charts/dapr/crds/subscription.yaml kubectl apply -f https://raw.githubusercontent.com/dapr/dapr/v1.19.0/charts/dapr/crds/resiliency.yaml kubectl apply -f https://raw.githubusercontent.com/dapr/dapr/v1.19.0/charts/dapr/crds/httpendpoints.yaml kubectl apply -f https://raw.githubusercontent.com/dapr/dapr/v1.19.0/charts/dapr/crds/mcpservers.yaml kubectl apply -f https://raw.githubusercontent.com/dapr/dapr/v1.19.0/charts/dapr/crds/workflowaccesspolicy.yamlhelm repo updatehelm upgrade dapr dapr/dapr --version 1.19.0 --namespace dapr-system --waitIf you’re using a values file, remember to add the
--valuesoption when running the upgrade command.*Ensure all pods are running:
kubectl get pods -n dapr-system -w NAME READY STATUS RESTARTS AGE dapr-operator-5cdd6b7f9c-9sl7g 1/1 Running 0 41s dapr-placement-server-0 1/1 Running 0 41s dapr-sentry-84565c747b-7bh8h 1/1 Running 0 35s dapr-sidecar-injector-68f868668f-6xnbt 1/1 Running 0 41sRestart your application deployments to update the Dapr runtime:
kubectl rollout restart deploy/<DEPLOYMENT-NAME>
Scheduler RBAC
From Dapr 1.19, the Scheduler needs get, list and watch on pods in the Dapr control plane namespace to detect whether a Placement service is deployed. It needs this on every 1.19 upgrade, whether or not you enable scheduler placement. The Helm chart and dapr upgrade -k add it as the dapr-scheduler-pods Role and RoleBinding in the release namespace. The dapr-scheduler ClusterRole (or Role, in namespaced RBAC mode) keeps namespaces only.
Upgrade existing Dapr deployment to enable high availability mode
Enable high availability mode in an existing Dapr deployment with a few additional steps.
Enable scheduler placement in an existing Dapr deployment
Actor placement can be served by the Scheduler service instead of the standalone Placement service by upgrading with global.scheduler.placement.enabled=true. The placement StatefulSet is removed by the same upgrade, and running sidecars adopt scheduler placement on their own: no sidecar restarts are needed, in either direction. The schedulers detect the placement pods leaving and take over within a few seconds; outside of this upgrade, a placement pod that crashes or restarts does not move the placement authority. Learn more about serving placement from the Scheduler service.
Important
Complete your Dapr version rollout before enabling this setting. Sidecars running an older Dapr version can only use the Placement service: with it undeployed, their Actor and Workflow APIs stall until the pod is upgraded to a version that supports scheduler placement. No actor state is lost. While a Placement service is running, it remains the placement authority: the Scheduler does not serve placement while one is present, so the cluster keeps a single placement authority throughout the rollout. Once no Placement service remains, the Scheduler serves placement, and a connected older sidecar is logged as a warning, since nothing is left to serve it.Before you enable it:
- Upgrade every sidecar to 1.19 or later: restart your applications after the control plane upgrade.
- Keep
global.scheduler.enabled=true,global.actors.enabled=trueandglobal.actors.serviceName=placement(the defaults). The Helm chart fails to render the setting without them. - If you manage RBAC outside the Helm chart, add the Scheduler RBAC Role.
Enable it with helm upgrade and --set global.scheduler.placement.enabled=true plus your usual values, or with the CLI:
dapr upgrade -k --runtime-version=1.19.0 --set global.scheduler.placement.enabled=true
The same upgrade restarts the Schedulers to pick up --placement-enabled=true, so expect actor and workflow activity to pause until the Schedulers are ready again, longer than the few seconds of the takeover itself.
To verify, check that sidecars log Connected to scheduler placement leader <address> and that exactly one Scheduler reports dapr_scheduler_placement_leader as 1. If dapr_scheduler_placement_incapable_sidecars is above 0, or a Scheduler logs A sidecar running an older Dapr version is connected, upgrade those pods: their actor and workflow APIs stall.
To roll back, upgrade with the setting false: the placement StatefulSet is redeployed and the schedulers hand placement back to it, again without sidecar restarts. In both directions, the change reassigns actors once because the two services place actors with different algorithms. Actor state is unaffected.
Because helm upgrade resets values not passed on the command line, pass the setting on every subsequent upgrade (or keep it in your values file), otherwise the placement StatefulSet is redeployed and placement hands back to it.
Downgrade
Warning
Do not roll back from 1.19 directly to 1.17.6 or earlier. The safe rollback target is 1.18.x.Dapr 1.19 still issues the Ed25519 workload identity keys that Dapr 1.18 introduced, and Sentry persists the Ed25519-keyed CA in the dapr-trust-bundle secret. Sentry versions before 1.17.7 cannot parse this bundle and crash on startup with:
fatal: error creating CA: failed to get CA bundle:
failed to verify CA bundle: unsupported key type ed25519.PrivateKey
| From | To | Result |
|---|---|---|
| 1.19 | 1.18.x | Safe. 1.18 and 1.19 write the same trust bundle format. |
| 1.19 | 1.17.7 or a later 1.17.x | Safe. 1.17.7 reads the Ed25519 trust bundle (#9904). |
| 1.19 | 1.17.6 or earlier | Sentry crash-loops at startup. Roll back to 1.17.7 first, confirm dapr status -k is healthy, then downgrade from there. |
A downgrade to 1.18.x or 1.17.7 needs no CA rotation. Rotate the CA only if your target is below 1.17.7 or your security posture requires it.
Downgrade in this order:
If you enabled scheduler placement, upgrade with
global.scheduler.placement.enabled=falseand wait for the Placement pods to be ready. Check that your sidecars logConnected to placement service: <address>.If you created
WorkflowAccessPolicyrules that userequires, remove or narrow them. A 1.18 control plane and sidecar do not know the field and apply those rules without the gate.Roll back the control plane with Helm:
helm history dapr -n dapr-system helm rollback dapr <REVISION> -n dapr-system --waitOr pin a specific version:
helm upgrade dapr dapr/dapr --version 1.18.7 --namespace dapr-system --waitRestart your application deployments, so the sidecars pick up the older injector image and get new identities from the rolled-back Sentry:
kubectl rollout restart deploy/<DEPLOYMENT-NAME>