Syncing Doppler secrets to local Docker containers
Local development drifts from production the moment someone hand-copies secrets into a .env and forgets to update it. Doppler closes that gap by injecting the same secrets into your container at runtime. This page wires it up for a Python service in Docker, extending Doppler for Multi-Cloud Secrets.
Problem 1: a hand-maintained .env in the image
# ANTI-PATTERN: secrets baked into the image, drifting from prod
COPY .env /app/.env # now in image history forever, and stale within a day
The secret is in the image layers and diverges from the real values the moment they rotate.
Problem 2: passing the token on the command line
# ANTI-PATTERN: token leaks into shell history and ps output
docker run -e DOPPLER_TOKEN=dp.st.dev.xxxx myapp
The token is now in shell history and visible in the process list to anyone on the host.
Secure implementation
# app/entrypoint.py — fetch at startup, never bake into the image
import os
import requests
from pydantic import SecretStr
def load_doppler() -> dict[str, SecretStr]:
token = os.environ["DOPPLER_TOKEN"] # injected by the runtime, not the image
resp = requests.get(
"https://api.doppler.com/v3/configs/config/secrets/download",
params={"format": "json"}, auth=(token, ""), timeout=5,
)
resp.raise_for_status()
return {k: SecretStr(v) for k, v in resp.json().items()}
# Run the container under the Doppler CLI; it injects DOPPLER_TOKEN from your login,
# never the image, never shell history.
doppler run --config dev -- docker compose up
doppler run supplies the scoped dev token to the container at launch. The image contains no secrets, and the same code path runs in CI and production with a different config.
Gotchas & version-specific behaviour
- Use a dev-scoped service token locally; never the production config.
doppler runinjects into the immediate process — for Compose, ensure the variable propagates to the service (environment:passthrough).- Set a request
timeoutso a Doppler outage fails fast instead of hanging the container. - Keep the token out of
docker history— inject at runtime, neverENVorARG.
Production parity checklist
- No
.envor secret is copied into the image. - The service token is dev-scoped and injected at runtime.
- The same fetch code runs locally, in CI, and in production.
- Fetched values are wrapped in
SecretStr. - Each environment maps to a distinct Doppler config.
Conclusion
doppler run injecting a dev-scoped token gives local Docker containers production-identical secrets without a committed file. For CI usage of the same mechanism, see Doppler Service Tokens in CI Pipelines.