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 run injects into the immediate process — for Compose, ensure the variable propagates to the service (environment: passthrough).
  • Set a request timeout so a Doppler outage fails fast instead of hanging the container.
  • Keep the token out of docker history — inject at runtime, never ENV or ARG.

Production parity checklist

  • No .env or 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.