This is the multi-page printable view of this section. Click here to print.
Overview of the Dapr services
1 - Dapr sidecar (daprd) overview
Dapr uses a sidecar pattern, meaning the Dapr APIs are run and exposed on a separate process, the Dapr sidecar, running alongside your application. The Dapr sidecar process is named daprd and is launched in different ways depending on the hosting environment.
The Dapr sidecar exposes:
- Building block APIs used by your application business logic
- A metadata API for discoverability of capabilities and to set attributes
- A health API to determine health status and sidecar readiness and liveness
The Dapr sidecar will reach readiness state once the application is accessible on its configured port. The application cannot access the Dapr components during application start up/initialization.

The sidecar APIs are called from your application over local http or gRPC endpoints.

Self-hosted with dapr run
When Dapr is installed in self-hosted mode, the daprd binary is downloaded and placed under the user home directory ($HOME/.dapr/bin for Linux/macOS or %USERPROFILE%\.dapr\bin\ for Windows).
In self-hosted mode, running the Dapr CLI run command launches the daprd executable with the provided application executable. This is the recommended way of running the Dapr sidecar when working locally in scenarios such as development and testing.
You can find the various arguments that the CLI exposes to configure the sidecar in the Dapr run command reference.
Kubernetes with dapr-sidecar-injector
On Kubernetes, the Dapr control plane includes the dapr-sidecar-injector service, which watches for new pods with the dapr.io/enabled annotation and injects a container with the daprd process within the pod. In this case, sidecar arguments can be passed through annotations as outlined in the Kubernetes annotations column in this table.
Native sidecars (Kubernetes 1.28+)
By default, daprd is injected as a regular container alongside your application. With Kubernetes native sidecars (KEP-753), daprd is instead injected as an init container with restartPolicy: Always. Refer to the Kubernetes sidecar containers documentation for details on behavior and lifecycle semantics.
Enable native sidecars globally via Helm:
dapr_sidecar_injector:
nativeSidecar: true
Or per-pod via annotation:
annotations:
dapr.io/enabled: "true"
dapr.io/enable-native-sidecar: "true"
Running the sidecar directly
In most cases you do not need to run daprd explicitly, as the sidecar is either launched by the CLI (self-hosted mode) or by the dapr-sidecar-injector service (Kubernetes). For advanced use cases (debugging, scripted deployments, etc.) the daprd process can be launched directly.
For a detailed list of all available arguments run daprd --help or see this table which outlines how the daprd arguments relate to the CLI arguments and Kubernetes annotations.
Examples
Start a sidecar alongside an application by specifying its unique ID.
Note:
--app-idis a required field, and cannot contain dots.daprd --app-id myappSpecify the port your application is listening to
daprd --app-id myapp --app-port 5000If you are using several custom resources and want to specify the location of the resource definition files, use the
--resources-pathargument:daprd --app-id myapp --resources-path <PATH-TO-RESOURCES-FILES>If you’ve organized your components and other resources (for example, resiliency policies, subscriptions, or configuration) into separate folders or a shared folder, you can specify multiple resource paths:
daprd --app-id myapp --resources-path <PATH-1-TO-RESOURCES-FILES> --resources-path <PATH-2-TO-RESOURCES-FILES>Enable collection of Prometheus metrics while running your app
daprd --app-id myapp --enable-metricsListen to IPv4 and IPv6 loopback only
daprd --app-id myapp --dapr-listen-addresses '127.0.0.1,[::1]'
2 - Dapr Operator control plane service overview
When running Dapr in Kubernetes mode, a pod running the Dapr Operator service manages Dapr component updates and provides Kubernetes services endpoints for Dapr.
Running the operator service
The operator service is deployed as part of dapr init -k, or via the Dapr Helm charts. For more information on running Dapr on Kubernetes, visit the Kubernetes hosting page.
Additional configuration options
The operator service includes additional configuration options.
Injector watchdog
The operator service includes an injector watchdog feature which periodically polls all pods running in your Kubernetes cluster and confirms that the Dapr sidecar is injected in those which have the dapr.io/enabled=true annotation. It is primarily meant to address situations where the Injector service did not successfully inject the sidecar (the daprd container) into pods.
The injector watchdog can be useful in a few situations, including:
Recovering from a Kubernetes cluster completely stopped. When a cluster is completely stopped and then restarted (including in the case of a total cluster failure), pods are restarted in a random order. If your application is restarted before the Dapr control plane (specifically the Injector service) is ready, the Dapr sidecar may not be injected into your application’s pods, causing your application to behave unexpectedly.
Addressing potential random failures with the sidecar injector, such as transient failures within the Injector service.
If the watchdog detects a pod that does not have a sidecar when it should have had one, it deletes it. Kubernetes will then re-create the pod, invoking the Dapr sidecar injector again.
The injector watchdog feature is disabled by default.
You can enable it by passing the --watch-interval flag to the operator command, which can take one of the following values:
--watch-interval=0: disables the injector watchdog (default value if the flag is omitted).--watch-interval=<interval>: the injector watchdog is enabled and polls all pods at the given interval; the value for the interval is a string that includes the unit. For example:--watch-interval=10s(every 10 seconds) or--watch-interval=2m(every 2 minutes).--watch-interval=once: the injector watchdog runs only once when the operator service is started.
If you’re using Helm, you can configure the injector watchdog with the dapr_operator.watchInterval option, which has the same values as the command line flags.
The injector watchdog is safe to use when the operator service is running in HA (High Availability) mode with more than one replica. In this case, Kubernetes automatically elects a “leader” instance which is the only one that runs the injector watchdog service.
However, when in HA mode, if you configure the injector watchdog to run “once”, the watchdog polling is actually started every time an instance of the operator service is elected as leader. This means that, should the leader of the operator service crash and a new leader be elected, that would trigger the injector watchdog again.
Watch this video for an overview of the injector watchdog:
3 - Dapr Placement control plane service overview
Deprecation notice
Starting with Dapr v1.19, actor placement can be served by the Scheduler service, enabled by the opt-in flagglobal.scheduler.placement.enabled=true (false by default). The standalone Placement service is planned for deprecation in Dapr v1.21, when the Scheduler serves actor placement by default.The Dapr Placement service is used to calculate and distribute distributed hash tables for the location of Dapr actors running in self-hosted mode or on Kubernetes. Grouped by namespace, the hash tables map actor types to pods or processes so a Dapr application can communicate with the actor. Anytime a Dapr application activates a Dapr actor, the Placement service updates the hash tables with the latest actor location.
Self-hosted mode
The Placement service Docker container is started automatically as part of dapr init, unless you initialize with dapr init --scheduler-placement, which has the Scheduler service serve actor placement instead. It can also be run manually as a process if you are running in slim-init mode.
Kubernetes mode
The Placement service is deployed as part of dapr init -k, or via the Dapr Helm charts. You can run Placement in high availability (HA) mode. Learn more about setting HA mode in your Kubernetes service.
Alternatively, actor placement can be served by the Scheduler service instead of the standalone Placement service. See Serving placement from the Scheduler service.
For more information on running Dapr on Kubernetes, visit the Kubernetes hosting page.
Serving placement from the Scheduler service
The Scheduler service can serve actor placement itself, so the standalone Placement service is not required to run.
In Kubernetes mode, set the Helm value:
global.scheduler.placement.enabled=true
With this setting, the Placement StatefulSet is not deployed and the Scheduler runs with --placement-enabled=true.
In self-hosted mode, initialize Dapr with dapr init --scheduler-placement (requires Dapr 1.19 or later). The Placement container is not started, and the CLI starts the Scheduler container with --placement-enabled=true for you. No further configuration is needed. The flag is not available with slim-init mode: dapr init --slim installs both the Placement and Scheduler binaries but does not run either of them, so you choose which one to run yourself, passing --placement-enabled=true if you run the Scheduler binary. Applications and sidecars need no configuration of their own: each sidecar takes actor placement from whichever service the control plane advertises, so the cluster always has exactly one placement authority. Toggling the setting in either direction requires no sidecar restarts.
The two services place actors with different algorithms: the Placement service uses a consistent hash ring, while the Scheduler uses rendezvous hashing. Both give the same guarantees (every sidecar deterministically agrees on a single host for each actor ID, and ownership spreads across hosts), but the resulting actor-to-host assignments differ. Switching the placement authority in either direction therefore reassigns actors once: affected actors are deactivated and reactivated on their new hosts with their state intact, as in any actor rebalance.
The Scheduler decides who the placement authority is by detecting whether a Placement service is deployed, so the switch is driven purely by deploying or removing the Placement service. In Kubernetes, the Scheduler watches for Placement pods and resolves the Placement service name; in every mode it also probes any Placement addresses the connected sidecars were configured with. Presence takes effect immediately, while absence must be confirmed by several consecutive checks: a Placement service that restarts or is briefly unreachable keeps the placement authority, and only removing it moves actor placement to the Scheduler.
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.The /placement/state API is only available from the standalone Placement service and is not served by the Scheduler.
Placement tables
There is an HTTP API /placement/state for Placement service that exposes placement table information. The API is exposed on the sidecar on the same port as the healthz. This is an unauthenticated endpoint, and is disabled by default. You need to set DAPR_PLACEMENT_METADATA_ENABLED environment or metadata-enabled command line args to true to enable it. If you are using helm you just need to set dapr_placement.metadataEnabled to true.
Important
When deploying actors into different namespaces (https://v1-19.docs.dapr.io/developing-applications/building-blocks/actors/namespaced-actors/), it is recommended to disable themetadata-enabled if you want to prevent retrieving actors from all namespaces. The metadata endpoint is scoped to all namespaces.Usecase:
The placement table API can be used to retrieve the current placement table, which contains all the actors registered across all namespaces. This is helpful for debugging and allowing tools to extract and present information about actors.
HTTP Request
GET http://localhost:<healthzPort>/placement/state
HTTP Response Codes
| Code | Description |
|---|---|
| 200 | Placement tables information returned |
| 500 | Placement could not return the placement tables information |
HTTP Response Body
Placement tables API Response Object
| Name | Type | Description |
|---|---|---|
| tableVersion | int | The placement table version |
| hostList | Actor Host Info[] | A json array of registered actors host info. |
| Name | Type | Description |
|---|---|---|
| name | string | The host:port address of the actor. |
| appId | string | app id. |
| actorTypes | json string array | List of actor types it hosts. |
| updatedAt | timestamp | Timestamp of the actor registered/updated. |
Examples
curl localhost:8080/placement/state
{
"hostList": [{
"name": "198.18.0.1:49347",
"namespace": "ns1",
"appId": "actor1",
"actorTypes": ["testActorType1", "testActorType3"],
"updatedAt": 1690274322325260000
},
{
"name": "198.18.0.2:49347",
"namespace": "ns2",
"appId": "actor2",
"actorTypes": ["testActorType2"],
"updatedAt": 1690274322325260000
},
{
"name": "198.18.0.3:49347",
"namespace": "ns2",
"appId": "actor2",
"actorTypes": ["testActorType2"],
"updatedAt": 1690274322325260000
}
],
"tableVersion": 1
}
Disabling the Placement service
The Placement service can be disabled with the following setting:
global.actors.enabled=false
The Placement service is not deployed with this setting in Kubernetes mode. This not only disables actor deployment, but also disables workflows, given that workflows use actors. This setting only applies in Kubernetes mode, however initializing Dapr with --slim excludes the Placement service from being deployed in self-hosted mode.
The Placement service is also not deployed when actor placement is served by the Scheduler service (global.scheduler.placement.enabled=true). Actors and workflows keep working in that case. See Serving placement from the Scheduler service.
For more information on running Dapr on Kubernetes, visit the Kubernetes hosting page.
Related links
4 - Dapr Scheduler control plane service overview
The Dapr Scheduler service is used to schedule different types of jobs, running in self-hosted mode or on Kubernetes.
- Jobs created through the Jobs API
- Actor reminder jobs (used by the actor reminders)
- Actor reminder jobs created by the Workflow API (which uses actor reminders): workflow start and wake-up reminders, timers, activity reminders, and, with the
WorkflowsFastPathpreview feature, one repeating janitor reminder per live workflow instance
The Scheduler service can also serve actor placement in place of the standalone Placement service. See Serving actor placement.
There is no leader Scheduler instance for jobs.
All Scheduler service replicas are considered peers.
All receive jobs to be scheduled for execution and the jobs are allocated between the available Scheduler service replicas for load balancing of the trigger events.
When the Scheduler serves actor placement, one replica is the placement leader, and its dapr_scheduler_placement_leader metric is 1.
The diagram below shows how the Scheduler service is used via the jobs API when called from your application. All the jobs that are tracked by the Scheduler service are stored in the Etcd database.

By default, Etcd is embedded in the Scheduler service, which means that the Scheduler service runs its own instance of Etcd. See Scheduler service flags for more information on how to configure the Scheduler service.
Job Locality
Default Job Behavior
When the Scheduler service triggers jobs, they are sent back to a single replica for the same app ID that scheduled the job in a randomly load balanced manner. This provides basic load balancing across your application’s replicas, which is suitable for most use cases where strict locality isn’t required.
Using Actor Reminders for Perfect Locality
For users who require perfect job locality (having jobs triggered on the exact same host that created them), actor reminders provide a solution. To enforce perfect locality for a job:
- Create an actor type with a random UUID that is unique to the specific replica
- Use this actor type to create an actor reminder
This approach ensures that the job will always be triggered on the same host which created it, rather than being randomly distributed among replicas.
Job Triggering
Job Failure Policy and Staging Queue
When the Scheduler service triggers a job and it has a client side error, the job is retried by default with a 1s interval and 3 maximum retries.
For non-client side errors, for example, when a job cannot be sent to an available Dapr sidecar at trigger time, it is placed in a staging queue within the Scheduler service. Jobs remain in this queue until a suitable sidecar instance becomes available, at which point they are automatically sent to the appropriate Dapr sidecar instance.
Self-hosted mode
The Scheduler service Docker container is started automatically as part of dapr init. It can also be run manually as a process if you are running in slim-init mode.
The Scheduler can be run in both high availability (HA) and non-HA modes in self-hosted deployments. However, non-HA mode is not recommended for production use. If switching between non-HA and HA modes, the existing data directory must be removed, which results in loss of jobs and actor reminders. Run a back-up before making this change to avoid losing data.
Kubernetes mode
The Scheduler service is deployed as part of dapr init -k, or via the Dapr Helm charts. Scheduler always runs in high availability (HA) mode in Kubernetes deployments. Scaling the Scheduler service replicas up or down is not possible without incurring data loss due to the nature of the embedded data store. Learn more about setting HA mode in your Kubernetes service.
When a Kubernetes namespace is deleted, all the Job and Actor Reminders corresponding to that namespace are deleted.
The Scheduler needs get, list and watch on pods in the Dapr control plane namespace to detect whether a Placement service is deployed. From Dapr 1.19, the Helm chart grants this with the dapr-scheduler-pods Role and RoleBinding in the release namespace, whether or not the Scheduler serves actor placement. The dapr-scheduler ClusterRole (or Role, with global.rbac.namespaced=true) keeps namespaces only. If you manage the control plane RBAC outside the Helm chart, add the pods permission before you upgrade to 1.19.
Docker Compose Example
Here’s how to expose the etcd ports in a Docker Compose configuration for standalone mode. When running in HA mode, you only need to expose the ports for one scheduler instance to perform backup operations.
version: "3.5"
services:
scheduler-0:
image: "docker.io/daprio/scheduler:1.19.0"
command:
- "./scheduler"
- "--etcd-data-dir=/var/run/dapr/scheduler"
- "--id=scheduler-0"
- "--etcd-initial-cluster=scheduler-0=http://scheduler-0:2380,scheduler-1=http://scheduler-1:2380,scheduler-2=http://scheduler-2:2380"
ports:
- 2379:2379
volumes:
- ./dapr_scheduler/0:/var/run/dapr/scheduler
scheduler-1:
image: "docker.io/daprio/scheduler:1.19.0"
command:
- "./scheduler"
- "--etcd-data-dir=/var/run/dapr/scheduler"
- "--id=scheduler-1"
- "--etcd-initial-cluster=scheduler-0=http://scheduler-0:2380,scheduler-1=http://scheduler-1:2380,scheduler-2=http://scheduler-2:2380"
volumes:
- ./dapr_scheduler/1:/var/run/dapr/scheduler
scheduler-2:
image: "docker.io/daprio/scheduler:1.19.0"
command:
- "./scheduler"
- "--etcd-data-dir=/var/run/dapr/scheduler"
- "--id=scheduler-2"
- "--etcd-initial-cluster=scheduler-0=http://scheduler-0:2380,scheduler-1=http://scheduler-1:2380,scheduler-2=http://scheduler-2:2380"
volumes:
- ./dapr_scheduler/2:/var/run/dapr/scheduler
Managing jobs with the Dapr CLI
Dapr provides a CLI for inspecting and managing all scheduled jobs, regardless of type. The CLI is the recommended way to view, back up, and delete jobs.
There are several different types of jobs which Scheduler manages:
app/{app-id}/{job-name}: Jobs created via the Jobs APIactor/{actor-type}/{actor-id}/{reminder-name}: Actor reminder jobs created via the Actor Reminders APIactivity/{app-id}/{instance-id}::{task-id}::0: Used internally for Workflow Activity remindersworkflow/{app-id}/{instance-id}/{random-name}: Used internally for Workflows.
From Dapr 1.19, activity job names always end in ::0. Earlier runtimes used the workflow instance’s generation as the last component, for example ::1 on the first run, so update scripts that build these names.
Please see here for how to manage specifically reminders with the CLI.
List jobs
dapr scheduler list
Example output:
NAME BEGIN COUNT LAST TRIGGER
actor/myactortype/actorid1/test1 -3.89s 1 2025-10-03T16:58:55Z
actor/myactortype/actorid2/test2 -3.89s 1 2025-10-03T16:58:55Z
app/test-scheduler/test1 -3.89s 1 2025-10-03T16:58:55Z
app/test-scheduler/test2 -3.89s 1 2025-10-03T16:58:55Z
activity/test-scheduler/xyz1::0::0 -888.8ms 0
activity/test-scheduler/xyz2::0::0 -888.8ms 0
workflow/test-scheduler/abc1/timer-0-TVIQGkvu +50.0h 0
workflow/test-scheduler/abc2/timer-0-OM2xqG9m +50.0h 0
For more detail, use the wide output format:
dapr scheduler list -o wide
NAMESPACE NAME BEGIN EXPIRATION SCHEDULE DUE TIME TTL REPEATS COUNT LAST TRIGGER
default actor/myactortype/actorid1/test1 2025-10-03T16:58:55Z @every 2h46m40s 2025-10-03T17:58:55+01:00 100 1 2025-10-03T16:58:55Z
default actor/myactortype/actorid2/test2 2025-10-03T16:58:55Z @every 2h46m40s 2025-10-03T17:58:55+01:00 100 1 2025-10-03T16:58:55Z
default app/test-scheduler/test1 2025-10-03T16:58:55Z @every 100m 2025-10-03T17:58:55+01:00 1234 1 2025-10-03T16:58:55Z
default app/test-scheduler/test2 2025-10-03T16:58:55Z 2025-10-03T19:45:35Z @every 100m 2025-10-03T17:58:55+01:00 10000s 56788 1 2025-10-03T16:58:55Z
default activity/test-scheduler/xyz1::0::0 2025-10-03T16:58:58Z 0s 0
default activity/test-scheduler/xyz2::0::0 2025-10-03T16:58:58Z 0s 0
default workflow/test-scheduler/abc1/timer-0-TVIQGkvu 2025-10-05T18:58:58Z 2025-10-05T18:58:58Z 0
default workflow/test-scheduler/abc2/timer-0-OM2xqG9m 2025-10-05T18:58:58Z 2025-10-05T18:58:58Z 0
Get job details
dapr scheduler get app/my-app/job1 -o yaml
Delete jobs
Delete one or more specific jobs:
dapr scheduler delete app/my-app/job1 actor/MyActor/123/reminder1
Bulk delete jobs with filters:
dapr scheduler delete-all all
dapr scheduler delete-all app/my-app
dapr scheduler delete-all actor/MyActorType
Backup and restore jobs
In production environments, it’s recommended to perform periodic backups of this data at an interval that aligns with your recovery point objectives.
The Dapr CLI provides a command for exporting all Scheduler data to a specific binary file.
Use the -k flag when running in Kubernetes mode.
dapr scheduler export -o scheduler-backup.bin
dapr scheduler export -k -o scheduler-backup.bin
To restore data from a backup file:
dapr scheduler import -f scheduler-backup.bin
dapr scheduler import -k -f scheduler-backup.bin
Monitoring Scheduler’s etcd Metrics
Port forward the Scheduler instance and view etcd’s metrics with the following:
curl -s http://localhost:2379/metrics
Fine tune the embedded etcd to your needs by reviewing and configuring the Scheduler’s etcd flags as needed.
Serving actor placement
The Scheduler can serve actor placement itself, so the standalone Placement service is not required to run.
In Kubernetes mode, set the Helm value:
global.scheduler.placement.enabled=true
With this value, the Helm chart fails to render unless global.scheduler.enabled=true, global.actors.enabled=true and global.actors.serviceName=placement (the defaults). The upgrade that sets the value removes the Placement StatefulSet, and the Schedulers take over placement within a few seconds. The same upgrade also restarts the Schedulers to pick up --placement-enabled=true, so expect actor and workflow activity to pause until the Schedulers are ready again.
In self-hosted mode, initialize Dapr with the --scheduler-placement flag, which skips the Placement container and starts the Scheduler with --placement-enabled=true:
dapr init --scheduler-placement
Sidecars take actor placement from whichever service the control plane advertises, so applications need no configuration of their own and the cluster always has exactly one placement authority. Toggling the setting in either direction requires no sidecar restarts. Learn more about serving placement from the Scheduler service.
To check which service a sidecar uses, look in its log for Connected to scheduler placement leader <address> (Scheduler) or Connected to placement service: <address> (Placement service). On the Scheduler, dapr_scheduler_placement_leader is 1 on the placement leader, dapr_scheduler_placement_streams_connected counts the connected sidecar placement streams, and dapr_scheduler_placement_incapable_sidecars counts connected sidecars older than 1.19. Upgrade those sidecars: without a Placement service, their actor and workflow APIs stall.
At startup, a sidecar waits for the Scheduler to advertise placement for up to --actors-placement-startup-timeout (annotation dapr.io/actors-placement-startup-timeout, default 30s), then falls back to the configured Placement service. The Scheduler advertises placement only after the sidecar’s jobs stream registers, so set the timeout higher than your app health probe budget.
The dissemination behavior of scheduler-served placement can be tuned with the following Scheduler flags:
--placement-enabled bool When enabled, this scheduler serves actor placement to daprd sidecars, replacing the standalone placement service. All scheduler replicas in the cluster must set the same value. (default false)
--placement-disseminate-timeout duration The timeout for a placement dissemination round to daprd sidecars. Sidecars which fail to acknowledge within the timeout have their placement stream closed. (default 8s)
--placement-disseminate-coalesce-window duration The window in which placement membership changes are coalesced into a single dissemination round. 0 disseminates immediately. (default 0)
Disabling the Scheduler service
If you are not using any features that require the Scheduler service (Jobs API, Actor Reminders, or Workflows), you can disable it by setting global.scheduler.enabled=false.
The Scheduler service cannot be disabled while it serves actor placement (global.scheduler.placement.enabled=true).
For more information on running Dapr on Kubernetes, visit the Kubernetes hosting page.
Failure behaviour
From Dapr 1.19, when the Scheduler server stops with a runtime error, for example because its embedded Etcd is briefly unavailable, the Scheduler recreates the server in the same process after a jittered backoff from 500 ms up to 10 s. Meanwhile its healthz endpoint reports not ready, and the log shows Scheduler server failed, recreating in <delay>. The process does not exit, so alerts that count Scheduler pod restarts do not see this condition: use readiness or the logs instead.
A port bind failure is the exception: the Scheduler process exits and Kubernetes restarts it. The Scheduler port, 50006, is inside the default Linux ephemeral port range, so one of the process’s own outbound connections can hold it. Without the exit, the replica would stay not ready until its liveness probe restarts it, 230 s with the chart defaults (180 s initial delay, then 5 failed probes 10 s apart).
Flag tuning
A number of Etcd flags are exposed on Scheduler which can be used to tune for your deployment use case.
External Etcd database
Scheduler can be configured to use an external Etcd database instead of the embedded one inside the Scheduler service replicas. It may be interesting to decouple the storage volume from the Scheduler StatefulSet or container, because of how the cluster or environment is administered or what storage backend is being used. It can also be the case that moving the persistent storage outside of the scheduler runtime completely is desirable, or there is some existing Etcd cluster provider which will be reused. Externalising the Etcd database also means that the Scheduler replicas can be horizontally scaled at will, however note that during scale events, job triggering will be paused. Scheduler replica count does not need to match the Etcd node count constraints.
To use an external Etcd cluster, set the --etcd-embed flag to false and provide the --etcd-client-endpoints flag with the endpoints of your Etcd cluster.
Optionally also include --etcd-client-username and --etcd-client-password flags for authentication if the Etcd cluster requires it.
--etcd-embed bool When enabled, the Etcd database is embedded in the scheduler server. If false, the scheduler connects to an external Etcd cluster using the --etcd-client-endpoints flag. (default true)
--etcd-client-endpoints stringArray Comma-separated list of etcd client endpoints to connect to. Only used when --etcd-embed is false.
--etcd-client-username string Username for etcd client authentication. Only used when --etcd-embed is false.
--etcd-client-password string Password for etcd client authentication. Only used when --etcd-embed is false.
Helm:
dapr_scheduler.etcdEmbed=true
dapr_scheduler.etcdClientEndpoints=[]
dapr_scheduler.etcdClientUsername=""
dapr_scheduler.etcdClientPassword=""
Etcd leadership election tuning
To improve the speed of election leadership of rescue nodes in the event of a failure, the following flag may be used to speed up the election process.
--etcd-initial-election-tick-advance Whether to fast-forward initial election ticks on boot for faster election. When it is true, then local member fast-forwards election ticks to speed up “initial” leader election trigger. This benefits the case of larger election ticks. Disabling this would slow down initial bootstrap process for cross datacenter deployments. Make your own tradeoffs by configuring this flag at the cost of slow initial bootstrap.
Helm:
dapr_scheduler.etcdInitialElectionTickAdvance=true
Storage tuning
The following options can be used to tune the embedded Etcd storage to the needs of your deployment. A deeper understanding of what these flags do can be found in the Etcd documentation.
Note
Changing these flags can greatly change the performance and behaviour of the Scheduler, so caution is advised when modifying them from the default set by Dapr. Changing these settings should always been done first in a testing environment, and monitored closely before applying to production.--etcd-backend-batch-interval string Maximum time before committing the backend transaction. (default "100ms")
--etcd-backend-batch-limit int Maximum operations before committing the backend transaction. (default 10000)
--etcd-compaction-mode string Compaction mode for etcd. Can be 'periodic' or 'revision' (default "revision")
--etcd-compaction-retention string Compaction retention for etcd. Can express time or number of revisions, depending on the value of 'etcd-compaction-mode' (default "10000")
--etcd-experimental-bootstrap-defrag-threshold-megabytes uint Minimum number of megabytes needed to be freed for etcd to consider running defrag during bootstrap. Needs to be set to non-zero value to take effect. (default 100)
--etcd-max-snapshots uint Maximum number of snapshot files to retain (0 is unlimited). (default 10)
--etcd-max-txn-ops uint Maximum number of operations permitted in a single etcd transaction. (default 10000)
--etcd-max-wals uint Maximum number of write-ahead logs to retain (0 is unlimited). (default 10)
--etcd-snapshot-count uint Number of committed transactions to trigger a snapshot to disk. (default 100000)
Helm:
dapr_scheduler.etcdBackendBatchInterval="100ms"
dapr_scheduler.etcdBackendBatchLimit=10000
dapr_scheduler.etcdCompactionMode="revision"
dapr_scheduler.etcdCompactionRetention="10000"
dapr_scheduler.etcdDefragThresholdMB=100
dapr_scheduler.etcdMaxSnapshots=10
dapr_scheduler.etcdMaxTxnOps=10000
From Dapr 1.19, the default compaction retention is 10000 revisions (earlier releases: 1000000). Every revision keeps the payload of the job it wrote, so under workflow load the old default grew the database to gigabytes of disk and memory. The retention bounds the number of revisions, not their size. A value you set yourself is kept, and helm upgrade --reuse-values keeps the old 1000000, so pass the new value or drop the override. A Scheduler watch that falls more than 10,000 revisions behind, for example during a leadership change under a write burst, is cancelled by Etcd. The Scheduler then re-reads the job list and reconnects, and job API calls wait until it is ready.
Healthz listen address
--healthz-listen-address string Address the healthz server listens on. Empty (the default) listens on all interfaces.
Set this flag to bind the Scheduler’s healthz endpoint to a specific interface, for example to keep it off a public network. It has no dedicated Helm value as of Dapr 1.19. In self-hosted mode, pass it directly to the Scheduler binary, for example in a Docker Compose or systemd configuration.
Related links
5 - Dapr Sentry control plane service overview
The Dapr Sentry service manages mTLS between services and acts as a certificate authority. It generates mTLS certificates and distributes them to any running sidecars. This allows sidecars to communicate with encrypted, mTLS traffic. For more information read the sidecar-to-sidecar communication overview.
On Kubernetes, Sentry also stamps the requesting pod’s container image references into each issued workload certificate as a custom X.509 extension, giving downstream systems verifiable supply-chain provenance of the software behind each workload identity.
Self-hosted mode
The Sentry service Docker container is not started automatically as part of dapr init. However it can be executed manually by following the instructions for setting up mutual TLS.
It can also be run manually as a process if you are running in slim-init mode.

Kubernetes mode
The sentry service is deployed as part of dapr init -k, or via the Dapr Helm charts. For more information on running Dapr on Kubernetes, visit the Kubernetes hosting page.

Additional sentry container arguments
Use the dapr_sentry.extraArgs Helm value to pass extra command-line arguments to the sentry container. Use this value for sentry flags that have no dedicated Helm value. The value is a list of strings. It defaults to []. Dapr appends each argument after the built-in sentry container arguments.
dapr_sentry:
extraArgs:
- "--jwt-ttl=48h"
- "--healthz-listen-address=127.0.0.1"
--healthz-listen-address sets the address the sentry healthz server listens on, and --metrics-listen-address sets the address of its metrics server. Both default to all interfaces.
Further reading
6 - Dapr Sidecar Injector control plane service overview
When running Dapr in Kubernetes mode, a pod is created running the Dapr Sidecar Injector service, which looks for pods initialized with the Dapr annotations, and then creates another container in that pod for the daprd service
Running the sidecar injector
The sidecar injector service is deployed as part of dapr init -k, or via the Dapr Helm charts. For more information on running Dapr on Kubernetes, visit the Kubernetes hosting page.
Authorized service accounts
The sidecar injector’s admission webhook only processes requests from authorized Kubernetes service accounts. This controls which controllers and service accounts are allowed to trigger sidecar injection when creating or updating pods.
By default, the injector authorizes a set of well-known Kubernetes controllers (such as replicaset-controller, deployment-controller, statefulset-controller, and others), as well as users in the system:masters group. You can authorize additional service accounts by configuring the dapr_sidecar_injector.allowedServiceAccounts Helm value.
If a pod creation request comes from a service account that is not authorized, the injector skips sidecar injection for that pod silently.
Configuration
Service accounts are specified in namespace:name format. Multiple entries can be comma-separated. Glob patterns are supported using Go’s path.Match syntax:
| Pattern | Description | Example |
|---|---|---|
* | Matches any sequence of characters | my-ns:* matches all service accounts in my-ns |
? | Matches any single character | staging-?:* matches staging-1, staging-a, etc. |
[...] | Matches a character class | proj-*:sa-[abc]* matches service accounts starting with sa-a, sa-b, or sa-c |
Examples
Configure via Helm:
helm install dapr dapr/dapr --namespace dapr-system \
--set dapr_sidecar_injector.allowedServiceAccounts="my-namespace:my-service-account,team-*:deploy-*"
Or in a Helm values file:
dapr_sidecar_injector:
allowedServiceAccounts: "my-namespace:my-service-account,team-*:deploy-*"
Pattern examples:
| Pattern | Matches |
|---|---|
my-ns:my-sa | Exact match: service account my-sa in namespace my-ns |
my-ns:* | All service accounts in namespace my-ns |
team-*:deploy-* | Service accounts starting with deploy- in namespaces starting with team- |
*:* | All service accounts in all namespaces |
Note
Thedapr_sidecar_injector.allowedServiceAccountsPrefixNames Helm value is deprecated as of v1.18. Migrate your entries to dapr_sidecar_injector.allowedServiceAccounts using glob patterns instead (for example, my-ns:my-prefix* replaces the previous prefix-matching behavior). The deprecated value still functions but logs a deprecation warning.