Overview
Dapr (Distributed Application Runtime) is a portable, event-driven runtime that simplifies building resilient, microservice-based applications. It runs as a sidecar process alongside your application and provides building blocks for common distributed system patterns including service invocation, state management, pub/sub messaging, bindings, actors, and observability.
Dapr is language-agnostic and communicates with applications via HTTP or gRPC APIs. It abstracts away infrastructure concerns by providing pluggable components — you can switch between Redis, PostgreSQL, Kafka, Azure Service Bus, and many other backends without changing application code. Dapr runs on Kubernetes, self-hosted environments, and edge devices.
Installation
Dapr CLI
# macOS
brew install dapr/tap/dapr-cli
# Linux
wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash
# Windows (PowerShell)
# powershell -Command "iwr -useb https://raw.githubusercontent.com/dapr/cli/master/install/install.ps1 | iex"
# Verify
dapr --version
Initialize Dapr
# Self-hosted mode (Docker containers for Redis, Zipkin, Placement)
dapr init
# Kubernetes mode
dapr init -k
# Slim init (no Docker dependencies)
dapr init --slim
# Check status
dapr status -k # Kubernetes
dapr dashboard # Open web dashboard
Building Blocks
| Block | Description | API Endpoint |
|---|
| Service Invocation | Call other services | /v1.0/invoke/{appId}/method/{method} |
| State Management | Key-value state store | /v1.0/state/{storeName} |
| Pub/Sub | Publish and subscribe | /v1.0/publish/{pubsubName}/{topic} |
| Bindings | External system triggers | /v1.0/bindings/{bindingName} |
| Actors | Virtual actor pattern | /v1.0/actors/{actorType}/{actorId} |
| Secrets | Secret store access | /v1.0/secrets/{storeName}/{key} |
| Configuration | Dynamic config | /v1.0/configuration/{storeName} |
| Distributed Lock | Mutual exclusion | /v1.0-alpha1/lock/{storeName} |
| Workflows | Durable workflows | /v1.0-beta1/workflows |
Running Applications
# Run an app with Dapr sidecar
dapr run --app-id myapp --app-port 3000 -- node app.js
# Run with specific config
dapr run --app-id myapp \
--app-port 3000 \
--dapr-http-port 3500 \
--dapr-grpc-port 50001 \
--components-path ./components \
--config ./config.yaml \
-- python app.py
# Run without an app (for testing)
dapr run --app-id myapp --dapr-http-port 3500
# List running apps
dapr list
# Stop an app
dapr stop myapp
# View logs
dapr logs --app-id myapp -k
Service Invocation
# Invoke a method on another service
curl http://localhost:3500/v1.0/invoke/order-service/method/orders \
-H "Content-Type: application/json" \
-d '{"orderId": "123"}'
# Invoke with POST
curl -X POST http://localhost:3500/v1.0/invoke/payment-service/method/pay \
-H "Content-Type: application/json" \
-d '{"amount": 99.99}'
# Python SDK
from dapr.clients import DaprClient
with DaprClient() as client:
response = client.invoke_method(
app_id='order-service',
method_name='orders',
data=json.dumps({'orderId': '123'}),
http_verb='GET',
)
print(response.text())
State Management
# Save state
curl -X POST http://localhost:3500/v1.0/state/statestore \
-H "Content-Type: application/json" \
-d '[{"key": "order-123", "value": {"status": "pending", "total": 99.99}}]'
# Get state
curl http://localhost:3500/v1.0/state/statestore/order-123
# Delete state
curl -X DELETE http://localhost:3500/v1.0/state/statestore/order-123
# Bulk get
curl -X POST http://localhost:3500/v1.0/state/statestore/bulk \
-H "Content-Type: application/json" \
-d '{"keys": ["order-123", "order-456"]}'
# Transaction
curl -X POST http://localhost:3500/v1.0/state/statestore/transaction \
-H "Content-Type: application/json" \
-d '{
"operations": [
{"operation": "upsert", "request": {"key": "k1", "value": "v1"}},
{"operation": "delete", "request": {"key": "k2"}}
]
}'
Pub/Sub Messaging
# Publish a message
curl -X POST http://localhost:3500/v1.0/publish/pubsub/orders \
-H "Content-Type: application/json" \
-d '{"orderId": "123", "status": "created"}'
# Subscribe (Python SDK)
from dapr.ext.grpc import App
import json
app = App()
@app.subscribe(pubsub_name='pubsub', topic='orders')
def order_handler(event):
data = json.loads(event.Data())
print(f"Received order: {data['orderId']}")
app.run(50051)
Subscription Configuration
# subscription.yaml
apiVersion: dapr.io/v1alpha1
kind: Subscription
metadata:
name: order-subscription
spec:
pubsubname: pubsub
topic: orders
route: /orders
deadLetterTopic: orders-dead
Component Configuration
State Store (Redis)
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
- name: actorStateStore
value: "true"
Pub/Sub (Redis)
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: pubsub
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
Pub/Sub (Kafka)
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: kafka-pubsub
spec:
type: pubsub.kafka
version: v1
metadata:
- name: brokers
value: "kafka:9092"
- name: consumerGroup
value: "mygroup"
- name: authType
value: "none"
Secret Store
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: secrets
spec:
type: secretstores.kubernetes
version: v1
Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "order-service"
dapr.io/app-port: "3000"
dapr.io/log-level: "info"
dapr.io/sidecar-cpu-request: "100m"
dapr.io/sidecar-memory-request: "64Mi"
dapr.io/config: "appconfig"
spec:
containers:
- name: order-service
image: myregistry/order-service:v1
ports:
- containerPort: 3000
Advanced Usage
Resiliency Policies
apiVersion: dapr.io/v1alpha1
kind: Resiliency
metadata:
name: myresiliency
spec:
policies:
retries:
retryForever:
policy: constant
duration: 5s
maxRetries: -1
retry3Times:
policy: exponential
maxInterval: 15s
maxRetries: 3
circuitBreakers:
simpleCB:
maxRequests: 1
interval: 30s
timeout: 60s
trip: consecutiveFailures >= 5
timeouts:
general: 5s
targets:
apps:
order-service:
retry: retry3Times
circuitBreaker: simpleCB
timeout: general
components:
statestore:
outbound:
retry: retryForever
Workflows
import dapr.ext.workflow as wf
wf_runtime = wf.WorkflowRuntime()
@wf_runtime.workflow(name='order_workflow')
def order_workflow(ctx: wf.DaprWorkflowContext, order: dict):
result = yield ctx.call_activity(validate_order, input=order)
if result['valid']:
yield ctx.call_activity(process_payment, input=order)
yield ctx.call_activity(ship_order, input=order)
return result
@wf_runtime.activity(name='validate_order')
def validate_order(ctx, order: dict):
return {'valid': order.get('amount', 0) > 0}
CLI Commands
| Command | Description |
|---|
dapr init | Initialize Dapr |
dapr run --app-id <id> -- <cmd> | Run app with sidecar |
dapr list | List running applications |
dapr stop <app-id> | Stop an application |
dapr dashboard | Open web dashboard |
dapr components -k | List components (K8s) |
dapr configurations -k | List configs (K8s) |
dapr status -k | Check Dapr system status |
dapr upgrade -k | Upgrade Dapr on K8s |
dapr uninstall -k | Uninstall from K8s |
Troubleshooting
| Issue | Solution |
|---|
| Sidecar not injecting | Verify dapr.io/enabled: "true" annotation; check Dapr operator logs |
| State store errors | Verify component config; check Redis/DB connectivity |
| Pub/sub messages lost | Check subscription route matches app endpoint; verify component config |
| Service invocation 404 | Verify target app-id matches; ensure target app is running |
| High latency | Check sidecar resource limits; tune component connection pools |
| Component not loading | Verify component YAML in components directory; check naming |
| Actor placement issues | Ensure placement service is running; check actor state store config |
| mTLS errors | Verify Sentry service is healthy; check certificate expiration |