Skip to content

Dapr Reference: Service Invocation, Pub/Sub, State Management & Distributed App Runtime

Dapr (Distributed Application Runtime) is a CNCF-graduated project that provides distributed systems building blocks as APIs — service invocation, pub/sub, state management, secrets, bindings, and actors — so your code doesn’t need to implement them. Works on K8s, self-hosted, or locally.

1. Core Building Blocks

What Dapr provides and when to use it
Building Block What it does Example use
Service Invocation Synchronous service-to-service calls with retries, mTLS, tracing frontend calls user-service via Dapr sidecar (no service discovery code needed)
Pub/Sub Async message publish/subscribe with pluggable brokers order-service publishes OrderCreated; inventory-service consumes it
State Management Key-value store API (Redis, Cosmos, PostgreSQL, etc.) session data, cart contents, last-known-value storage
Secrets Secret store API (Vault, AWS SM, K8s secrets) app reads DB password via Dapr — not direct Vault SDK
Bindings Trigger code from or send to external systems (SQS, Kafka, cron, HTTP) process S3 upload events, send email on order, scheduled job
Actors Virtual actor model for stateful, turn-based processing IoT device state, saga orchestration, reminder-based workflows
# Install Dapr CLI:
brew install dapr/tap/dapr    # macOS
dapr init                     # local mode (runs Redis + Zipkin via Docker)
dapr init -k                  # Kubernetes mode (installs Dapr control plane)

# Verify K8s install:
kubectl get pods -n dapr-system    # dapr-operator, dapr-sidecar-injector, dapr-scheduler, etc.
dapr status -k                     # all component health

2. Service Invocation

Call other services via Dapr — no service discovery, auto mTLS, retries
# Enable Dapr sidecar for a Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-frontend
spec:
  template:
    metadata:
      annotations:
        dapr.io/enabled: "true"          # inject Dapr sidecar
        dapr.io/app-id: "frontend"       # this app's Dapr name
        dapr.io/app-port: "8080"         # port your app listens on
        dapr.io/config: "tracing"        # optional: tracing config
spec:
  template:
    metadata:
      annotations:
        dapr.io/enabled: "true"
        dapr.io/app-id: "user-service"   # the service being called

# Call another service via Dapr sidecar (from frontend code):
# HTTP: POST http://localhost:3500/v1.0/invoke/{app-id}/method/{method}
curl http://localhost:3500/v1.0/invoke/user-service/method/users/123

# Python example:
import requests
resp = requests.get("http://localhost:3500/v1.0/invoke/user-service/method/users/123")
# Dapr handles: service discovery, mTLS, retries, distributed tracing

# No SDK needed — plain HTTP to localhost:3500 (Dapr sidecar port)
# Dapr routes the call to the right pod regardless of where it's running

# Configure retries + timeout:
apiVersion: dapr.io/v1alpha1
kind: Resiliency
metadata:
  name: user-service-resiliency
spec:
  targets:
    apps:
      user-service:
        timeout: 10s
        retry:
          policy: constant
          duration: 1s
          maxRetries: 3
        circuitBreaker:
          maxRequests: 1
          interval: 10s
          timeout: 30s
          trip: consecutiveFailures > 3

3. Pub/Sub

Async messaging with pluggable brokers — swap Redis for Kafka with zero code change
# Pub/Sub component (Redis Streams — default for local dev):
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: pubsub
  namespace: default
spec:
  type: pubsub.redis
  version: v1
  metadata:
    - name: redisHost
      value: redis:6379
    - name: redisPassword
      secretKeyRef:
        name: redis-secret
        key: redis-password

# Swap to Kafka with zero app code changes:
spec:
  type: pubsub.kafka
  version: v1
  metadata:
    - name: brokers
      value: kafka:9092
    - name: consumerGroup
      value: my-group

# Publish a message (HTTP POST to Dapr sidecar):
curl -X POST http://localhost:3500/v1.0/publish/pubsub/orders   -H "Content-Type: application/json"   -d '{"orderId": "123", "customerId": "456"}'

# Subscribe (app endpoint Dapr calls when message arrives):
# 1. Register subscription in dapr/subscriptions.yaml:
apiVersion: dapr.io/v1alpha1
kind: Subscription
metadata:
  name: orders-subscription
spec:
  pubsubname: pubsub
  topic: orders
  route: /orders    # Dapr POSTs to this endpoint on your app

# 2. Your app handles it (Python Flask example):
@app.route("/orders", methods=["POST"])
def handle_order():
    data = request.json
    print(f"Processing order {data['orderId']}")
    return json.dumps({"status": "SUCCESS"}), 200

4. State Management

Key-value state store with pluggable backends
# State store component:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
spec:
  type: state.redis           # swap to state.postgresql, state.azure.cosmosdb, etc.
  version: v1
  metadata:
    - name: redisHost
      value: redis:6379

# Get, set, delete state (via Dapr HTTP API):
# Set state:
curl -X POST http://localhost:3500/v1.0/state/statestore   -H "Content-Type: application/json"   -d '[{"key": "user-123-cart", "value": {"items": ["product-1", "product-2"]}}]'

# Get state:
curl http://localhost:3500/v1.0/state/statestore/user-123-cart

# Delete state:
curl -X DELETE http://localhost:3500/v1.0/state/statestore/user-123-cart

# Transactions (multi-key atomic operation):
curl -X POST http://localhost:3500/v1.0/state/statestore/transaction   -H "Content-Type: application/json"   -d '{
    "operations": [
      {"operation": "upsert", "request": {"key": "order-1", "value": {"status": "paid"}}},
      {"operation": "upsert", "request": {"key": "inventory-1", "value": {"stock": 99}}}
    ]
  }'

# ETags for optimistic concurrency (prevent lost updates):
# GET returns ETag in header
# Include ETag in PUT to fail if state was changed by another process

5. Secrets & Local Development

Secret store API and running Dapr locally with dapr run
# Secret store component (K8s secrets as source):
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: kubernetes
spec:
  type: secretstores.kubernetes
  version: v1

# Read a secret via Dapr API (your app never talks to K8s API directly):
curl http://localhost:3500/v1.0/secrets/kubernetes/my-app-secrets
# Returns: {"db-password": "...", "api-key": "..."}

# Restrict which secrets an app can read (RBAC in Dapr):
apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
  name: app-config
spec:
  secrets:
    scopes:
      - storeName: kubernetes
        defaultAccess: deny             # deny by default
        allowedSecrets: [db-password]   # only this secret is accessible

# Run app locally with Dapr sidecar (no K8s needed):
dapr run   --app-id my-service   --app-port 8080   --dapr-http-port 3500   -- python app.py

# Run with custom components directory:
dapr run   --app-id my-service   --components-path ./dapr/components \  # local component YAML files
  -- python app.py

# List running Dapr apps:
dapr list            # local
dapr list -k         # Kubernetes

# Dashboard:
dapr dashboard -k    # opens web UI with app topology + component status

Track Dapr, Kubernetes, and microservice runtime releases.
ReleaseRun monitors Kubernetes, Docker, and 13+ DevOps technologies.

Related: NATS Reference | Apache Kafka Reference | External Secrets Operator Reference

🔍 Free tool: K8s YAML Security Linter — check your Dapr-enabled K8s workload manifests for 12 security misconfigurations.

Founded

2023 in London, UK

Contact

hello@releaserun.com