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.