Skip to main content
Updated Feb 23, 2026

Secrets and Configuration

Your Task API needs an API key to call an external service. You hardcode it in your Docker image. Then you realize everyone who pulls your image can extract that key. You move it to an environment variable. Better, but now your deployment YAML has the key in plain text. Anyone with cluster access can read your manifests.

This isn't a hypothetical concern. In production systems, credential leaks cause real damage. API keys get revoked, databases get accessed by unauthorized parties, and teams spend days rotating compromised secrets across dozens of services.

Dapr's secrets building block solves this by providing a unified API to access secrets from multiple stores---Kubernetes secrets, HashiCorp Vault, Azure Key Vault, AWS Secrets Manager---without your application knowing which store is being used. This lesson shows you how to retrieve secrets securely in your Python code and reference them in component configurations.


Creating Kubernetes Secrets

Before Dapr can retrieve secrets, they must exist in Kubernetes. The kubectl create secret command creates secrets from literal values or files.

Create a Secret for API Credentials

kubectl create secret generic api-credentials \
--from-literal=api-key=my-secret-key-123 \
--from-literal=api-secret=my-secret-value-456

Output:

secret/api-credentials created

Verify the Secret Exists

kubectl get secret api-credentials -o yaml

Output:

apiVersion: v1
kind: Secret
metadata:
name: api-credentials
namespace: default
type: Opaque
data:
api-key: bXktc2VjcmV0LWtleS0xMjM=
api-secret: bXktc2VjcmV0LXZhbHVlLTQ1Ng==

Notice the values are base64-encoded (not encrypted). Kubernetes secrets provide access control, not encryption at rest by default.

Create a Redis Password Secret

For later use with component YAML:

kubectl create secret generic redis-password \
--from-literal=password=redis-secret-pass

Output:

secret/redis-password created

The Kubernetes Secrets Store Component

Dapr needs a secrets store component to know where to retrieve secrets from. The Kubernetes secrets store is built-in---no extra installation required.

Component YAML

# components/kubernetes-secrets.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: kubernetes-secrets
namespace: default
spec:
type: secretstores.kubernetes
version: v1
metadata: []

This component:

  • type: secretstores.kubernetes tells Dapr to use the Kubernetes Secrets API
  • metadata: Empty because Dapr runs inside Kubernetes and has native access
  • name: Your code references this name when calling get_secret()

Deploy the Component

kubectl apply -f components/kubernetes-secrets.yaml

Output:

component.dapr.io/kubernetes-secrets created

Verify Component Registration

kubectl get components

Output:

NAME                  AGE
kubernetes-secrets 5s
statestore 1h
pubsub 1h

Retrieving Secrets with DaprClient

The Secrets API endpoint is /v1.0/secrets/{store}/{key}. With the Python SDK, you use DaprClient.get_secret().

Synchronous Pattern

from dapr.clients import DaprClient

with DaprClient() as client:
secret = client.get_secret(
store_name='kubernetes-secrets',
key='api-credentials'
)
api_key = secret.secret.get('api-key')
print(f"Got API key: {api_key[:4]}...")

Output:

Got API key: my-s...

Async Pattern with Context Manager

For FastAPI applications, use the async pattern:

from dapr.clients.aio import DaprClient
from fastapi import FastAPI

app = FastAPI()

@app.get("/config")
async def get_config():
async with DaprClient() as client:
secret = await client.get_secret(
store_name='kubernetes-secrets',
key='api-credentials'
)
api_key = secret.secret.get('api-key')
return {"api_key_prefix": api_key[:4] + "..."}

Output:

{"api_key_prefix": "my-s..."}

Getting Specific Keys from a Secret

Kubernetes secrets can contain multiple key-value pairs. The get_secret() response includes all keys:

async with DaprClient() as client:
secret = await client.get_secret(
store_name='kubernetes-secrets',
key='api-credentials'
)

# Access individual keys
api_key = secret.secret.get('api-key')
api_secret = secret.secret.get('api-secret')

print(f"Key: {api_key}")
print(f"Secret: {api_secret}")

Output:

Key: my-secret-key-123
Secret: my-secret-value-456

Getting Bulk Secrets

To retrieve all secrets from a store:

async with DaprClient() as client:
secrets = await client.get_bulk_secret(
store_name='kubernetes-secrets'
)

for secret_name, values in secrets.secrets.items():
print(f"{secret_name}: {list(values.keys())}")

Output:

api-credentials: ['api-key', 'api-secret']
redis-password: ['password']

Referencing Secrets in Component YAML

Hard-coding credentials in component YAML defeats the purpose of secrets. Dapr supports secretKeyRef to pull values from a secrets store.

The Pattern

Instead of:

metadata:
- name: redisPassword
value: "my-plain-text-password" # BAD: exposed in YAML

Use:

metadata:
- name: redisPassword
secretKeyRef:
name: redis-password # Kubernetes secret name
key: password # Key within the secret

Complete Example: Redis State Store with Secret Password

# components/statestore.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
namespace: default
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: redis-master.default.svc.cluster.local:6379
- name: redisPassword
secretKeyRef:
name: redis-password
key: password
auth:
secretStore: kubernetes-secrets

The auth.secretStore field tells Dapr which secrets store to use for resolving secretKeyRef values. Without this, Dapr doesn't know where to look up the secret.

Kafka Pub/Sub with SASL Credentials

# components/kafka-pubsub.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: kafka-pubsub
namespace: default
spec:
type: pubsub.kafka
version: v1
metadata:
- name: brokers
value: kafka-cluster.kafka.svc.cluster.local:9092
- name: authType
value: password
- name: saslUsername
secretKeyRef:
name: kafka-credentials
key: username
- name: saslPassword
secretKeyRef:
name: kafka-credentials
key: password
auth:
secretStore: kubernetes-secrets

Configuration API Overview

Secrets are for sensitive data that rarely changes. Configuration is for dynamic settings that may change at runtime---feature flags, rate limits, threshold values.

When to Use Each API

ScenarioUseWhy
Database passwordSecretsSensitive, access-controlled
API keySecretsSensitive, access-controlled
Feature flagConfigurationNon-sensitive, may change frequently
Rate limit valueConfigurationNon-sensitive, may need runtime updates
Service URLConfigurationNon-sensitive, environment-specific
Encryption keySecretsHighly sensitive

Configuration API Endpoint

GET /v1.0/configuration/{store}?key=feature-x&key=max-retries

Python SDK Usage

async with DaprClient() as client:
config = await client.get_configuration(
store_name='configstore',
keys=['feature-flag-x', 'max-retry-count']
)

for item in config.items:
print(f"{item.key}: {item.value}")

Output:

feature-flag-x: enabled
max-retry-count: 3

Configuration vs Secrets: Key Differences

AspectSecrets APIConfiguration API
PurposeSensitive credentialsDynamic settings
EncryptionAt rest (depends on store)Not required
CachingTypically cachedCan subscribe to changes
Access patternRead on startupRead + subscribe
ExamplesPasswords, API keys, certificatesFeature flags, thresholds

Practical Integration: Task API with Secrets

Let's integrate secrets into the Task API from previous lessons.

The Scenario

Your Task API needs:

  1. API key for an external notification service
  2. Redis password for state store (via secretKeyRef)

Step 1: Create the Secret

kubectl create secret generic notification-api \
--from-literal=api-key=notif-key-abc123

Step 2: Update Your FastAPI Application

from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException
from dapr.clients.aio import DaprClient
from pydantic import BaseModel
import json

class NotificationConfig:
api_key: str | None = None

notification_config = NotificationConfig()

@asynccontextmanager
async def lifespan(app: FastAPI):
# Load secrets on startup
async with DaprClient() as client:
secret = await client.get_secret(
store_name='kubernetes-secrets',
key='notification-api'
)
notification_config.api_key = secret.secret.get('api-key')
print(f"Loaded notification API key: {notification_config.api_key[:4]}...")
yield

app = FastAPI(lifespan=lifespan)

@app.post("/tasks/{task_id}/notify")
async def notify_task_owner(task_id: str):
if not notification_config.api_key:
raise HTTPException(status_code=500, detail="Notification service not configured")

# Use the API key to call external service
# In production: httpx.post(url, headers={"Authorization": f"Bearer {notification_config.api_key}"})
return {"status": "notification_sent", "task_id": task_id}

Output (on startup):

Loaded notification API key: noti...

Step 3: Update State Store Component with secretKeyRef

# components/statestore.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: redis-master.default.svc.cluster.local:6379
- name: redisPassword
secretKeyRef:
name: redis-password
key: password
auth:
secretStore: kubernetes-secrets

Now your state store password comes from Kubernetes secrets, not plain text YAML.


Reflect on Your Skill

You built a dapr-deployment skill in Lesson 0. Test and improve it based on what you learned.

Test Your Skill

Using my dapr-deployment skill, show me how to retrieve API credentials from Kubernetes secrets using Dapr's async Python client.

Does your skill include the get_secret() pattern?

Identify Gaps

Ask yourself:

  • Does my skill explain the Kubernetes secrets store component?
  • Does it show how to use secretKeyRef in component YAML?
  • Can it help me decide between Secrets API and Configuration API?

Improve Your Skill

If you found gaps:

My dapr-deployment skill is missing secrets management patterns.
Update it to include:
1. Kubernetes secrets store component (secretstores.kubernetes)
2. DaprClient.get_secret() async pattern
3. secretKeyRef for referencing secrets in component YAML
4. auth.secretStore field requirement

Try With AI

You now understand Dapr's secrets and configuration building blocks. Use AI to apply these patterns to your own systems.

Setup

Open your AI assistant with context about your Dapr project. These prompts help you implement secure credential management.

Prompt 1: Retrieve Secrets in Your Application

Configure my Task API to get an API key from Kubernetes secrets via Dapr using the async DaprClient.

Requirements:
- Secret name: external-service-credentials
- Key within secret: api-key
- Load on application startup using FastAPI lifespan
- Make available to route handlers without re-fetching

Show me the complete implementation with error handling.

What you're learning: The startup-load pattern ensures secrets are available throughout your application's lifetime without repeated API calls. The lifespan context manager is the modern FastAPI pattern for initialization.

Prompt 2: Reference Secrets in Component YAML

Show me how to reference secrets in a Dapr component YAML using secretKeyRef.

I have:
- Kubernetes secret: database-credentials
- Keys in secret: username, password
- Component: state.postgresql

Show me the complete component YAML with:
1. Host from plain value
2. Username and password from secretKeyRef
3. The auth.secretStore configuration

What you're learning: The secretKeyRef pattern keeps sensitive data out of your YAML files. The auth.secretStore field is required for Dapr to know which secrets store resolves the references.

Prompt 3: Secrets vs Configuration Decision

What's the difference between Dapr's Secrets API and Configuration API? When should I use each?

I have these values to manage:
- Database connection string with password
- Feature flag for new notification system
- Maximum retry count for failed API calls
- API key for payment processor
- Threshold for alert notifications

For each, tell me which API to use and why.

What you're learning: Secrets are for sensitive data with access control requirements. Configuration is for dynamic settings that may need runtime updates or subscriptions. Mixing them up creates security risks or unnecessary complexity.

Safety Note

Never log full secret values. The pattern api_key[:4] + "..." shown in this lesson reveals only enough to confirm the right secret was loaded. In production, consider not logging secret prefixes at all---an attacker could use timing attacks or log access to reconstruct secrets. Test secret retrieval in development, then remove debug logging before deployment.