Skip to main content

Secrets Management

All sensitive configuration is managed through Doppler as the single source of truth, synced into the cluster via the External Secrets Operator (ESO).

Architecture​

How It Works​

1. Doppler as Source of Truth​

All secrets are stored in Doppler projects with environment-specific configs. Developers manage secrets through the Doppler dashboard or CLI — never directly in Kubernetes manifests.

2. ClusterSecretStore​

A single ClusterSecretStore resource connects the cluster to Doppler:

apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: doppler-secret-store
spec:
provider:
doppler:
auth:
secretRef:
dopplerToken:
name: doppler-token-auth
key: dopplerToken
namespace: external-secrets

3. ExternalSecret Resources​

Each application that needs secrets has an ExternalSecret that maps Doppler keys to Kubernetes Secret keys:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: bookmarked-secrets
namespace: default
spec:
refreshInterval: 1m
secretStoreRef:
name: doppler-secret-store
kind: ClusterSecretStore
target:
name: bookmarked-secrets
creationPolicy: Owner
data:
- secretKey: DATABASE_URL
remoteRef:
key: DATABASE_URL
- secretKey: JWT_SECRET
remoteRef:
key: JWT_SECRET

4. Pod Consumption​

Pods reference the generated Kubernetes Secret through envFrom or individual valueFrom references:

containers:
- name: server
envFrom:
- secretRef:
name: bookmarked-secrets

Secret Categories​

CategoryExamplesStorage
Database credentialsDATABASE_URL, connection stringsDoppler → ExternalSecret
API keysGitHub tokens, service API keysDoppler → ExternalSecret
JWT secretsSigning keys for authenticationDoppler → ExternalSecret
Docker registryDockerHub credentials for CIGitHub Actions secrets
ArgoCDAdmin credentials, repo tokensDoppler → ExternalSecret
TLS certificatesLet's Encrypt certscert-manager (automated)

Refresh and Rotation​

  • Refresh interval: ExternalSecrets poll Doppler every 1 minute
  • Rotation workflow: Update the secret in Doppler → ESO syncs the new value → pods pick up changes on next restart
  • No manual kubectl: Secrets are never created or edited with kubectl create secret

What's NOT in Doppler​

Some values are intentionally kept as plain YAML in deployment manifests:

ValueReason
PROMETHEUS_URLInternal cluster DNS, not sensitive
PORTApplication port number
NODE_ENVEnvironment identifier
ALLOWED_ORIGINSCORS origins, publicly visible in responses
Design Principle

If a value would be harmful if exposed publicly, it goes in Doppler. If it's just configuration that could appear in a README, it stays in YAML.