Dapr: A Distributed Application Runtime Tutorial (2026)

Dapr: A Distributed Application Runtime Tutorial (2026)

Dapr: A Distributed Application Runtime Tutorial (2026)

Most microservice teams end up writing the same distributed-systems plumbing over and over: retry logic wrapped around HTTP calls, a custom pub/sub abstraction on top of Kafka, a secrets-fetching helper that leaks into business logic, an actor framework that nobody fully understands. This Dapr tutorial exists to show you a different approach. Dapr — the Distributed Application Runtime — relocates that plumbing into a language-agnostic sidecar process that exposes a stable HTTP and gRPC API to your application. Your Python service calls localhost:3500/v1.0/state/mystore rather than importing a Redis client, configuring connection pools, and handling sentinel failover yourself. The underlying component can swap from Redis to Azure Cosmos DB without a code change — just a component YAML edit. What this covers: Dapr’s sidecar architecture; how to install and run it locally; every major building block with working config and code; Dapr on Kubernetes; how it compares to a service mesh; honest trade-offs; and practical recommendations grounded in production experience.


Context: What Dapr Is and Why the Distributed-Runtime Idea Matters

Distributed systems force application teams into an uncomfortable choice: embed infrastructure SDKs directly into application code, or build an internal platform layer that every team must adopt and maintain. Both paths accumulate debt. The first scatters Redis clients, Kafka producers, and Vault readers across dozens of services in three languages. The second creates a bespoke framework that diverges from upstream, breaks on upgrades, and becomes the primary source of on-call pages.

Dapr proposes a third option: a portable runtime that lives beside your process as a sidecar and exposes a normalized API for the common patterns every distributed application needs. The application side of the contract is a simple HTTP or gRPC call to localhost. The infrastructure side is a pluggable component model driven by YAML configuration files. The sidecar handles retries, circuit-breaking, mTLS, tracing propagation, and SDK differences so your code does not have to.

Microsoft open-sourced Dapr in 2019. It graduated to a CNCF Incubating project and has since reached CNCF Graduated status, a signal that the project has met production-readiness and governance standards the foundation requires. As of mid-2026 the runtime is actively maintained with a release cadence of roughly one minor version per quarter, and the ecosystem has expanded to include building blocks for actors, workflow orchestration, configuration management, and an emerging Conversation API aimed at AI agent patterns.

The core thesis of this post is that Dapr’s value is real but conditional: it pays off when you need portable, language-heterogeneous distributed primitives and are willing to absorb the operational cost of running a sidecar per pod. When those conditions are not met — when your stack is homogeneous, your team is small, or your infrastructure is constrained — the abstraction leakage and operational overhead can outweigh the benefits. Both sides get a fair hearing below.


Architecture and Setup

The Sidecar Model

Every Dapr-enabled application runs alongside a daprd process. In local development this is a second process on your machine; on Kubernetes it is an injected container in the same pod. The two communicate over localhost — the application sends HTTP requests to localhost:3500 (the default Dapr HTTP port) or connects via gRPC on localhost:50001. The sidecar, in turn, talks to the actual infrastructure components: Redis, Kafka, HashiCorp Vault, Azure Service Bus, AWS S3, or any of the hundreds of components in the Dapr component registry.

App, Dapr sidecar, and pluggable infrastructure components
Figure 1: An application communicates only with its local Dapr sidecar over HTTP or gRPC. The sidecar owns all connections to state stores, message brokers, and secrets backends, configured via component YAML files.

The key architectural consequence is that your application code has zero knowledge of which infrastructure product is backing each building block. The contract is the Dapr API, not the Redis API. That is the portability promise — and also the source of the abstraction leakage risk discussed later.

Installing Dapr: Self-Hosted Mode

Self-hosted mode is how you run Dapr on a developer laptop or a plain Linux host without Kubernetes. The Dapr CLI handles the scaffolding.

# Install the Dapr CLI (Linux / macOS)
wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash

# Initialize Dapr in self-hosted mode
# This pulls the daprd binary, a Redis container, and a Zipkin container
dapr init

# Verify
dapr --version

After dapr init, three things are running locally: the daprd runtime binary, a Redis container (used as the default state store and pub/sub component), and a Zipkin container for distributed tracing. Default component files are written to ~/.dapr/components/.

Running an application with the Dapr sidecar attached uses dapr run:

# Run a Python app with a Dapr sidecar
# --app-id  : the logical name of this service (used for service discovery)
# --app-port: the port your app listens on
# --dapr-http-port: the port daprd exposes to your app (default 3500)
dapr run --app-id order-service \
         --app-port 8080 \
         --dapr-http-port 3500 \
         -- python app.py

Component YAML: The Configuration Contract

Every infrastructure capability is described by a component YAML file. Dapr reads these at startup and wires the sidecar accordingly. The schema is consistent across building blocks: apiVersion, kind: Component, a metadata.name the app references by string, and a spec block with type and metadata key-value pairs.

# ~/.dapr/components/statestore.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
  namespace: default
spec:
  type: state.redis
  version: v1
  metadata:
    - name: redisHost
      value: "localhost:6379"
    - name: redisPassword
      secretKeyRef:
        name: redis-secret
        key: password
    - name: actorStateStore
      value: "true"

The secretKeyRef field shows how Dapr pulls sensitive values from a secrets component rather than embedding them as plaintext. This referencing pattern is consistent across all components that need credentials.


The Building Blocks with Examples

Dapr organizes its capabilities into discrete building blocks. Each is independently usable — you do not have to adopt all of them. Here is a practical tour of the most important ones with working examples.

Service invocation flow through two Dapr sidecars with mTLS
Figure 2: Service invocation. The caller’s sidecar resolves the target service name, establishes a mutual-TLS gRPC connection to the target sidecar, and forwards the HTTP call to the target application on its localhost port.

Service Invocation

Service invocation replaces direct HTTP calls between services with a sidecar-mediated call that adds service discovery, mTLS, retries, and distributed tracing automatically. The caller addresses the target by its app-id, not by hostname or IP.

# Illustrative — caller service (Python, using the requests library directly)
import requests

# Call the "checkout" service's /orders endpoint via the local Dapr sidecar
response = requests.post(
    "http://localhost:3500/v1.0/invoke/checkout/method/orders",
    json={"item": "widget", "quantity": 3},
    headers={"Content-Type": "application/json"}
)
response.raise_for_status()
print(response.json())

The sidecar resolves checkout to the actual pod address using its configured name-resolution component (mDNS in self-hosted mode; Kubernetes DNS in cluster mode). The connection between the two sidecars uses mutual TLS issued by the Sentry CA. Your application code never touches TLS certificates, service registries, or retry budgets.

The receiving service is a plain HTTP server. The sidecar routes the call to the app on its configured port:

# Illustrative — callee service (Python/Flask)
from flask import Flask, request, jsonify
app = Flask(__name__)

@app.route("/orders", methods=["POST"])
def create_order():
    data = request.json
    # Business logic here
    return jsonify({"order_id": "abc123", "status": "accepted"}), 200

if __name__ == "__main__":
    app.run(port=8080)

State Management

The state management building block gives services a key-value store API. The backing store — Redis, Cosmos DB, PostgreSQL, DynamoDB, and many others — is swapped by changing the component YAML without touching application code.

# Illustrative — save and retrieve state via the Dapr HTTP API
import requests, json

BASE_URL = "http://localhost:3500/v1.0/state/statestore"

# Save state
requests.post(BASE_URL, json=[
    {"key": "cart-user-42", "value": {"items": ["widget", "gadget"]}}
])

# Read state
resp = requests.get(f"{BASE_URL}/cart-user-42")
cart = resp.json()
print(cart)

# Delete state
requests.delete(f"{BASE_URL}/cart-user-42")

Dapr supports optimistic concurrency via ETags, which are returned in the response and can be included in subsequent writes to prevent lost-update races. It also supports transactional multi-key operations for components that support transactions.

Pub/Sub Messaging

Pub/sub decouples producers and consumers through a message broker. Dapr normalizes the publish side to a single POST call and manages subscriptions declaratively.

Pub/sub flow from publisher through a broker to a subscriber via Dapr sidecars
Figure 3: The publisher POSTs to its local sidecar, which delivers the message to the configured broker. The subscriber’s sidecar polls or listens to the broker and pushes messages to the subscriber application’s registered HTTP handler endpoint.

# Illustrative — publish a message
import requests

requests.post(
    "http://localhost:3500/v1.0/publish/pubsub/orders",
    json={"order_id": "xyz789", "total": 49.95},
    headers={"Content-Type": "application/json"}
)

The subscriber registers its topic handlers in one of two ways. Programmatic subscription is done at startup via a GET endpoint the sidecar calls:

# Illustrative — subscriber (Python/Flask) with programmatic subscription
from flask import Flask, request, jsonify
app = Flask(__name__)

@app.route("/dapr/subscribe", methods=["GET"])
def subscribe():
    return jsonify([{
        "pubsubname": "pubsub",
        "topic": "orders",
        "route": "/orders"
    }])

@app.route("/orders", methods=["POST"])
def handle_order():
    order = request.json
    print(f"Processing order {order['order_id']}")
    return jsonify({"status": "SUCCESS"}), 200

Returning HTTP 200 with {"status": "SUCCESS"} acknowledges the message. Returning {"status": "RETRY"} tells the sidecar to redeliver. Returning {"status": "DROP"} discards it without redelivery — useful for poison messages.

Alternatively, declarative subscriptions are defined in YAML files loaded by the sidecar, which keeps routing configuration out of application code entirely.

# components/subscription.yaml
apiVersion: dapr.io/v2alpha1
kind: Subscription
metadata:
  name: orders-subscription
spec:
  pubsubname: pubsub
  topic: orders
  routes:
    default: /orders
  deadLetterTopic: orders-dlq

Bindings: Connecting to External Systems

Input bindings trigger your app from external events (a cron schedule, an incoming Kafka message, an S3 object-created notification). Output bindings let your app push to external systems — S3, SendGrid, Twilio — without embedding their SDKs.

# components/s3-binding.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: s3output
spec:
  type: bindings.aws.s3
  version: v1
  metadata:
    - name: bucket
      value: "my-reports-bucket"
    - name: region
      value: "us-east-1"
    - name: accessKey
      secretKeyRef:
        name: aws-secret
        key: accessKey
    - name: secretKey
      secretKeyRef:
        name: aws-secret
        key: secretKey
# Illustrative — output binding: upload a file to S3
import requests, base64

content = base64.b64encode(b"report content here").decode()
requests.post(
    "http://localhost:3500/v1.0/bindings/s3output",
    json={
        "data": content,
        "metadata": {"key": "reports/2026-06-19.txt"},
        "operation": "create"
    }
)

Actors

The virtual actor model implements the Orleans pattern: actors are single-threaded, addressable by a string ID, and automatically activated on first access. They are useful for per-entity state machines — IoT device shadows, order state machines, game sessions.

# Illustrative — invoking an actor method via the Dapr HTTP API
import requests

actor_type = "DeviceShadow"
actor_id   = "sensor-node-007"

requests.put(
    f"http://localhost:3500/v1.0/actors/{actor_type}/{actor_id}/method/UpdateReading",
    json={"temperature": 22.4, "humidity": 65}
)

Actors maintain state through the Dapr state management building block and support timers (fire-once delayed callbacks) and reminders (durable callbacks that survive sidecar restarts). Placement is handled by the Dapr Placement Service, which distributes actor instances evenly across the available sidecars.

Secrets Management

The secrets building block provides a normalized API for fetching secrets from HashiCorp Vault, Kubernetes Secrets, AWS Secrets Manager, Azure Key Vault, and others.

# Illustrative — retrieve a secret at runtime
import requests

resp = requests.get(
    "http://localhost:3500/v1.0/secrets/vault/db-password"
)
secret = resp.json()["db-password"]

Secrets can also be referenced inside component YAML (as shown in the statestore.yaml above), meaning your infrastructure components never need plaintext credentials in their definitions.

Workflow

Dapr’s Workflow building block, which reached general availability in the v1.13 release cycle, provides durable execution for long-running business processes. Workflows are expressed as code (using SDK abstractions over the underlying workflow engine) and survive process restarts because their state is checkpointed after every activity step.

# Illustrative — defining a workflow with two activity steps (Python SDK)
from dapr.ext.workflow import WorkflowRuntime, DaprWorkflowContext, WorkflowActivityContext

def order_workflow(ctx: DaprWorkflowContext, order_id: str):
    yield ctx.call_activity(validate_order, input=order_id)
    yield ctx.call_activity(charge_payment, input=order_id)

def validate_order(ctx: WorkflowActivityContext, order_id: str):
    # idempotent validation logic
    return {"valid": True}

def charge_payment(ctx: WorkflowActivityContext, order_id: str):
    # idempotent payment logic
    return {"charged": True}

Workflows support sub-workflows, fan-out/fan-in patterns, compensation (saga rollback), and timers that survive long pauses — minutes, hours, or days — without holding a thread.

Configuration

The configuration building block provides real-time configuration reads with optional subscriptions for live updates:

# Read configuration keys and watch for changes
resp = requests.get(
    "http://localhost:3500/v1.0/configuration/configstore?key=feature-flag-new-checkout"
)
config = resp.json()

This integrates cleanly with backends like Azure App Configuration or Kubernetes ConfigMaps, keeping feature flag reads out of bespoke polling loops in application code.


Dapr on Kubernetes and the Service Mesh Comparison

Kubernetes Deployment

On Kubernetes, Dapr installs a control plane into its own namespace and injects the daprd sidecar container into annotated pods automatically.

# Add the Dapr Helm chart repository
helm repo add dapr https://dapr.github.io/helm-charts/
helm repo update

# Install Dapr into the dapr-system namespace
helm upgrade --install dapr dapr/dapr \
  --namespace dapr-system \
  --create-namespace \
  --set global.ha.enabled=true \
  --wait

A pod opts into Dapr injection with annotations:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  template:
    metadata:
      annotations:
        dapr.io/enabled: "true"
        dapr.io/app-id: "order-service"
        dapr.io/app-port: "8080"
        dapr.io/log-level: "info"
        dapr.io/enable-api-logging: "true"
    spec:
      containers:
        - name: order-service
          image: myregistry/order-service:latest
          ports:
            - containerPort: 8080

When this pod starts, the Dapr Sidecar Injector webhook injects the daprd container. Components are deployed as Kubernetes Custom Resources (CRDs) managed by the Dapr Operator.

Dapr control plane on Kubernetes: operator, sidecar injector, placement, and sentry
Figure 4: The Dapr control plane on Kubernetes consists of four components — the Operator (manages Component CRDs), the Sidecar Injector (webhook that injects daprd), the Placement Service (actor instance distribution), and Sentry (issues short-lived mTLS certificates to every sidecar).

# Kubernetes Component CRD for a Redis state store
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
  namespace: production
spec:
  type: state.redis
  version: v1
  metadata:
    - name: redisHost
      value: "redis-master.redis.svc.cluster.local:6379"
    - name: redisPassword
      secretKeyRef:
        name: redis-credentials
        key: password

Dapr vs a Service Mesh

This is the most common positioning question, and the answer depends on what you are actually trying to solve. Dapr and a service mesh are addressing different layers.

A service mesh — Istio, Linkerd, Cilium Service Mesh (see our Cilium 1.17 service mesh deep-dive) — operates transparently at the network layer. It intercepts all TCP traffic regardless of protocol, adds mTLS, enforces traffic policies, and provides telemetry, without any application code changes. It knows nothing about what the traffic means semantically.

Dapr operates at the application layer. It is protocol-aware, building-block-aware, and requires the application to call the Dapr API explicitly. It provides semantic capabilities a network proxy cannot: state management, pub/sub, actor placement, workflow orchestration, secrets fetching. But it requires an API contract with the application — it is not transparent.

The two are not mutually exclusive. Many production deployments run both: Cilium or Istio handles network-level policy and zero-trust mTLS between pods (including non-Dapr pods), while Dapr provides the application-layer building blocks for services that need them. Dapr’s own mTLS (via Sentry) can be disabled in environments where the mesh already provides it, avoiding double-encryption overhead.

If your only need is traffic management, retries, and observability across service-to-service HTTP calls, a service mesh is lighter and more transparent. If you need portable distributed primitives — state, pub/sub, actors, secrets — across a polyglot stack, Dapr is doing something fundamentally different.


Observability

Dapr emits OpenTelemetry-compatible traces, metrics, and logs from the sidecar without any instrumentation required in application code. Configure a tracing exporter in the Dapr configuration resource:

# Dapr Configuration for OpenTelemetry trace export
apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
  name: dapr-config
  namespace: production
spec:
  tracing:
    samplingRate: "1"
    otel:
      endpointAddress: "http://otel-collector.observability.svc.cluster.local:4318"
      isSecure: false
      protocol: http
  metric:
    enabled: true
  logging:
    apiLogging:
      enabled: true

Every service-to-service invocation, every state operation, every pub/sub publish and deliver generates a span automatically. Dapr propagates W3C Trace Context headers so spans link correctly across service boundaries in Jaeger, Grafana Tempo, or any OpenTelemetry-compatible backend.

Key sidecar metrics to monitor include dapr_http_server_request_count, dapr_component_operation_duration_ms, dapr_grpc_io_server_completed_rpcs, and dapr_resiliency_policy_count (the last is especially useful to verify that retry and circuit-breaker policies are active). Dapr exposes a Prometheus-compatible metrics endpoint on port 9090 of the sidecar by default.

The Dapr Dashboard (dapr dashboard) provides a local web UI for inspecting running applications, active components, and actor placement in self-hosted mode.

For GitOps-managed Dapr configuration alongside your application manifests, see our ArgoCD and Flux GitOps for industrial fleets guide.


Trade-offs and What Goes Wrong

Dapr’s thesis is compelling on paper. In production it introduces costs that need to be planned for, not discovered.

The sidecar operational tax. Every pod carries an extra container — daprd — that consumes CPU and memory. Published sidecar resource consumption is in the range of a few hundred megabytes of memory and measurable CPU at load, but actual overhead depends on message volume and which building blocks are active. At thousands of pods, this adds up to material infrastructure cost. Teams that adopted Kubernetes to maximize container density find Dapr sidecars partially reversing those gains. Operators must size the sidecar container’s resource requests and limits deliberately, monitor sidecar restarts, and handle the operational surface of an extra process per pod.

Abstraction leakage. The component abstraction does not cover every feature of every backend. Kafka’s consumer group semantics, Redis Streams’ consumer group ACK model, and DynamoDB’s conditional writes all have nuances that Dapr’s normalized API may not fully expose. When you need a backend-specific capability — a Kafka header, a Redis SCAN, a DynamoDB projection expression — you either abandon the Dapr abstraction for that call or you do without. This is not unique to Dapr; every abstraction layer leaks eventually. But teams that believe the component-swap promise is zero-cost are usually disappointed the first time they try to swap Redis for DynamoDB and discover the eviction and consistency semantics differ enough to break their application logic.

Component version drift. Dapr’s component registry includes community-contributed components of varying maintenance quality. Core components (Redis, Kafka, Postgres) are actively maintained by the Dapr maintainers. Niche components may lag, break on newer versions of the backing service, or have open CVEs. Treat community-maintained components as you would any third-party dependency: review the maintenance history, pin versions, and validate upgrades in staging.

gRPC or HTTP overhead on the hot path. The Dapr sidecar communicates with the application over localhost, which means the serialization and context-switch cost of HTTP/gRPC is present on every operation — even a state read that would be a single function call in a native Redis client. For latency-sensitive inner loops, this overhead can be significant. Benchmark your specific workload before committing; the Dapr SDK clients (available in Go, Python, Java, .NET, JavaScript, Rust) use gRPC, which is substantially faster than HTTP for high-throughput paths.

Dapr is not a service mesh — and filling that gap yourself is work. Dapr handles mTLS for service invocation between Dapr-enabled services. Non-Dapr services in the same cluster are outside this trust boundary unless you layer a service mesh. A naive deployment where some services use Dapr and others do not creates a mixed trust posture that is harder to reason about than either pure Dapr or pure mesh.

For teams building internal developer platforms where Dapr is one of many infrastructure concerns, our Crossplane Composition Functions guide covers how to provision Dapr-compatible infrastructure components (state stores, brokers) as self-service platform abstractions.


Practical Recommendations

Dapr is best adopted with a clear-eyed view of what it buys and what it costs. The following recommendations distill the patterns that separate successful Dapr adoptions from the ones that get quietly ripped out after six months.

Start with a single building block in a single non-critical service before rolling Dapr out fleet-wide. State management is the easiest entry point: it has a simple API, immediate observability benefits, and concrete portability value. Service invocation is the most tempting but also the most operationally complex, because it replaces existing load-balancer patterns that engineers understand with a sidecar-mediated path that is harder to debug without Dapr tooling.

Run Dapr in high-availability mode in production. The control plane components — Operator, Placement, Sentry — should each run with at least two replicas and their own pod disruption budgets. A Placement Service restart causes a brief pause in actor activation; Sentry restarts trigger certificate re-issuance. Both are recoverable, but they surface as latency spikes that are confusing without prior documentation.

Treat resiliency policies as mandatory configuration, not optional. Dapr’s resiliency API (policy CRDs or JSON config) allows you to declare retry, timeout, and circuit-breaker behavior per component and per service-to-service route. Define these before deploying to production — the default behavior on transient failures is a single attempt with no retry, which is rarely what you want.

Sidecar resource governance matters at scale. Set explicit resources.requests and resources.limits on the daprd sidecar container via the dapr.io/sidecar-cpu-request, dapr.io/sidecar-memory-request, and corresponding limit annotations. Without these, the sidecar will consume burstable resources and trigger OOMKilled events under load.

Test component swaps in CI, not in production. Create a test harness that runs your service against both the production component (e.g., Kafka) and an in-process substitute (e.g., Redis Streams) to validate that your pub/sub handlers work under both semantics. This is the only reliable way to catch abstraction leakage before it reaches customers.

Pre-production Dapr readiness checklist:

  • [ ] All components pinned to explicit versions in YAML (version: v1)
  • [ ] Secrets referenced via secretKeyRef, never as plaintext value in component YAML
  • [ ] Resiliency policy CRDs defined for every component and critical service invocation path
  • [ ] Sidecar resource requests and limits set via pod annotations
  • [ ] Control plane running in HA mode with pod disruption budgets
  • [ ] OpenTelemetry trace export configured and verified end-to-end in staging
  • [ ] Dapr Dashboard and Prometheus scrape confirmed functional
  • [ ] Component swap tested in CI pipeline against at least two backing implementations
  • [ ] mTLS enforced (Sentry running); if mesh also present, confirm double-encryption budget
  • [ ] Upgrade runbook documented: test sidecar version upgrade in staging before fleet rollout

FAQ

What is Dapr and how is it different from a service mesh like Istio?

Dapr is an application-layer runtime that provides building blocks for state, pub/sub, service invocation, actors, secrets, and workflow through an explicit HTTP/gRPC API your code calls. A service mesh like Istio operates transparently at the network layer, intercepting all traffic without application code changes, providing mTLS, traffic shaping, and observability. Dapr is semantic and requires opt-in from the application; a service mesh is protocol-transparent and applies universally across the cluster. They complement each other and are commonly deployed together.

Does Dapr add significant latency to every service call?

Yes, a measurable overhead exists because every operation goes through localhost HTTP or gRPC serialization rather than a direct in-process SDK call. For typical service-to-service invocations the added latency is in the single-digit millisecond range when using the gRPC path with a Dapr SDK client. For high-throughput, latency-sensitive inner loops — thousands of state reads per second per service — you should benchmark against a native SDK before committing. The overhead is real and should be part of your architecture decision, not a surprise in production.

Can I use Dapr with any programming language?

Dapr’s HTTP API is language-agnostic by design — any process that can make an HTTP or gRPC call can use it. Official SDKs exist for Go, Python, Java, .NET, JavaScript and TypeScript, and Rust. The SDKs use gRPC under the hood for better performance, wrap common patterns, and provide actor and workflow abstractions. For languages without an SDK, the raw HTTP API works fully; you lose some ergonomic sugar but retain all capabilities.

What happens if the Dapr sidecar crashes or restarts?

The sidecar is a separate container process. If it crashes, the application container keeps running but loses the ability to make Dapr API calls until the sidecar restarts. Kubernetes will restart the sidecar container according to its restart policy. During the gap, calls to localhost:3500 fail with connection refused. Well-designed applications should treat the Dapr API as an external dependency and apply the same retry and circuit-breaker logic they would for any downstream service. Dapr’s own resiliency policies do not protect against a sidecar-down scenario — that is an application-level concern.

Is Dapr suitable for edge or IoT workloads?

Dapr has a documented edge deployment mode and has been used in IoT gateway scenarios where a constrained host runs a small number of microservices. The sidecar footprint is heavier than embedded SDKs, but the portability benefits — swap MQTT bindings for HTTP bindings without code changes, normalize device state into a portable state store API — are meaningful in heterogeneous edge environments. The actors building block maps naturally to per-device digital twin patterns. For resource-constrained single-board computers, the overhead is a harder sell; for edge Kubernetes clusters (K3s, MicroK8s) the fit is much better.

How does Dapr handle secrets rotation?

Dapr fetches secrets from the configured secrets store at runtime, not at startup only. Components that reference secrets via secretKeyRef re-read from the secrets backend on each sidecar restart. For live rotation without restarts, applications can call the Dapr secrets API directly and re-initialize their connections on a rotation event. Integration with external rotation workflows (AWS Secrets Manager auto-rotation, Vault dynamic secrets) requires the application to either poll the Dapr secrets API periodically or subscribe to a configuration-change notification via the configuration building block.


Further Reading

The Dapr official documentation is the most authoritative source for building block specifications, component configurations, SDK references, and the resiliency API. The CNCF project page for Dapr provides governance information, security audit reports, and project maturity details. The Dapr component registry on GitHub documents every available component with its configuration schema, supported operations, and known limitations — essential reading before committing to a specific component in production.

For platform engineering context around Dapr, see our Crossplane Composition Functions and internal developer platform guide — Crossplane’s provider model can provision the backing infrastructure (Redis clusters, Kafka topics, Vault namespaces) that Dapr components reference, giving you a complete infrastructure-as-code story from broker creation through to Dapr component YAML generation.

For the network layer that Dapr sits on top of, the Cilium 1.17 service mesh tutorial covers eBPF-accelerated mTLS, network policy, and observability — all of which compose with Dapr’s application-layer capabilities without duplication.

GitOps delivery of Dapr component CRDs and application manifests is covered in ArgoCD and Flux for industrial GitOps fleets, including how to structure Component YAML in a Git repository so that environment promotion (dev → staging → production) flows through pull-request approval rather than imperative kubectl apply.


Sources: Dapr overview — docs.dapr.io; State of Dapr 2026 — Diagrid Blog; Dapr releases — GitHub; CNCF Dapr project.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *