- Never expose secret values.
- Never print secrets to stdout/stderr.
- Never include secret values in LLM/tool message bodies, prompts, or files.
- Never serialize secrets into files, stack traces, debug dumps, or shell history.
- When commands carry tokens, set
export HISTIGNORE='doppler*:export DOPPLER_TOKEN*' first.
- Prefer env injection over value retrieval.
- Primary pattern:
doppler run --project=<p> --config=<c> -- <command>.
- Application reads injected env vars internally.
- Avoid
doppler secrets get / doppler secrets download unless strictly required, and never log the output.
- Bind repos with a committed
doppler.yaml.
- Project-root
doppler.yaml lets doppler setup --no-interactive work for teammates and CI.
- Per-user state lives in
~/.doppler/.doppler.yaml and must NOT be hand-edited.
Detection:
if ! command -v doppler >/dev/null 2>&1; then
echo "doppler CLI missing — installing" >&2
# run an install branch from the table below
fi
doppler --version
OS-specific install (use the first matching branch):
macOS (Homebrew):
brew install dopplerhq/cli/doppler
Linux — Debian/Ubuntu (apt, signed repo, persistent):
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg
curl -sLf --retry 3 --tlsv1.2 --proto "=https" \
'https://packages.doppler.com/public/cli/gpg.DE2A7741A397C129.key' \
| sudo gpg --dearmor -o /usr/share/keyrings/doppler-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/doppler-archive-keyring.gpg] https://packages.doppler.com/public/cli/deb/debian any-version main" \
| sudo tee /etc/apt/sources.list.d/doppler-cli.list
sudo apt-get update && sudo apt-get install -y doppler
Linux — RHEL/CentOS/Fedora (yum/dnf):
sudo rpm --import 'https://packages.doppler.com/public/cli/gpg.DE2A7741A397C129.key'
curl -sLf --retry 3 --tlsv1.2 --proto "=https" \
'https://packages.doppler.com/public/cli/config.rpm.txt' \
| sudo tee /etc/yum.repos.d/doppler-cli.repo
sudo dnf install -y doppler || sudo yum install -y doppler
Linux/macOS fallback or CI — official install script:
(curl -Ls --tlsv1.2 --proto "=https" --retry 3 https://cli.doppler.com/install.sh \
|| wget -t 3 -qO- https://cli.doppler.com/install.sh) | sh
Windows (PowerShell, winget):
winget install doppler
Docker / containers (no package manager): bake into the image — see templates/Dockerfile.doppler.snippet.
Authoritative sources (consult only if you suspect a stale flag):
After install, verify:
doppler --version
Do NOT proceed if the CLI is still missing — report the install failure and stop.
────────────────────────────────────────────────────────
PART A — AD HOC SCRIPTS
────────────────────────────────────────────────────────
────────────────────────────────────────────────────────
PART B — PROJECTS / LONG-LIVED APPLICATIONS
────────────────────────────────────────────────────────
For team-wide reproducibility, also commit a doppler.yaml at the repo root (see templates/doppler.yaml):
setup:
- project: my-app
config: dev
Monorepo variant:
setup:
- path: backend/
project: my-app-api
config: dev
- path: frontend/
project: my-app-web
config: dev
Teammates and CI then run:
doppler setup --no-interactive
Create:
# interactive setup, then mint
doppler setup
doppler configs tokens create ci-deploy --plain
# or single-shot
doppler configs tokens create ci-deploy \
--project my-app --config prd --plain
Ephemeral token (auto-expires):
DOPPLER_TOKEN=$(doppler configs tokens create job-$(date +%s) \
--project my-app --config prd --max-age 5m --plain)
Revoke:
doppler configs tokens revoke -p my-app -c prd dp.st.prd.xxxx
Auth precedence (highest → lowest): --token flag → DOPPLER_TOKEN env → --project/--config flags → directory scope in ~/.doppler/.doppler.yaml.
Pattern B — host-side injection (no Doppler in image):
doppler run -- docker compose up
Never COPY .env into an image. Never bake DOPPLER_TOKEN into a layer.
Doppler Kubernetes Operator (recommended) — syncs Doppler configs into native Secret objects; pods consume via envFrom. Install via Helm; see https://docs.doppler.com/docs/kubernetes-operator. The Operator handles rotation; no doppler binary in the image.
doppler run inside containers using DOPPLER_TOKEN from a K8s Secret:
kubectl create secret generic doppler-token \
--from-literal=DOPPLER_TOKEN='dp.st.prd.xxxx'
spec:
containers:
- name: app
image: my-app:latest
envFrom:
- secretRef:
name: doppler-token
Prefer (1) for production. Use (2) when the Operator is not available.
────────────────────────────────────────────────────────
COMMON FAILURES & FIXES
────────────────────────────────────────────────────────
────────────────────────────────────────────────────────
VERIFICATION CHECKLIST
────────────────────────────────────────────────────────
1---2name: doppler-secrets3description: Use the Doppler CLI to access and manage secrets safely in both ad hoc scripts and long-lived projects. Covers CLI install, env injection, project binding via doppler.yaml, environments, service tokens, Docker, Kubernetes, and CI/CD integration. Use when a task touches secrets and must avoid exposing them in logs, prompts, command history, or tool output.4---56<objective>7Provide a single, authoritative workflow for using the Doppler CLI to handle secrets safely across ad hoc scripting and long-lived projects.8</objective>910<essential_rules>111) Doppler CLI only.12- Never use the Doppler MCP for secret access.13- Use the `doppler` CLI for all reads, writes, validation, and runtime injection.14152) Never expose secret values.16- Never print secrets to stdout/stderr.17- Never include secret values in LLM/tool message bodies, prompts, or files.18- Never serialize secrets into files, stack traces, debug dumps, or shell history.19- When commands carry tokens, set `export HISTIGNORE='doppler*:export DOPPLER_TOKEN*'` first.20213) Prefer env injection over value retrieval.22- Primary pattern: `doppler run --project=<p> --config=<c> -- <command>`.23- Application reads injected env vars internally.24- Avoid `doppler secrets get` / `doppler secrets download` unless strictly required, and never log the output.25264) Bind repos with a committed `doppler.yaml`.27- Project-root `doppler.yaml` lets `doppler setup --no-interactive` work for teammates and CI.28- Per-user state lives in `~/.doppler/.doppler.yaml` and must NOT be hand-edited.29</essential_rules>3031<step_0_install_hedge>32Always check the CLI is installed before any other step. If missing, install via the OS-appropriate method below, then re-check.3334Detection:35```bash36if ! command -v doppler >/dev/null 2>&1; then37 echo "doppler CLI missing — installing" >&238 # run an install branch from the table below39fi40doppler --version41```4243OS-specific install (use the first matching branch):4445- macOS (Homebrew):46 ```bash47 brew install dopplerhq/cli/doppler48 ```4950- Linux — Debian/Ubuntu (apt, signed repo, persistent):51 ```bash52 sudo apt-get update53 sudo apt-get install -y apt-transport-https ca-certificates curl gnupg54 curl -sLf --retry 3 --tlsv1.2 --proto "=https" \55 'https://packages.doppler.com/public/cli/gpg.DE2A7741A397C129.key' \56 | sudo gpg --dearmor -o /usr/share/keyrings/doppler-archive-keyring.gpg57 echo "deb [signed-by=/usr/share/keyrings/doppler-archive-keyring.gpg] https://packages.doppler.com/public/cli/deb/debian any-version main" \58 | sudo tee /etc/apt/sources.list.d/doppler-cli.list59 sudo apt-get update && sudo apt-get install -y doppler60 ```6162- Linux — RHEL/CentOS/Fedora (yum/dnf):63 ```bash64 sudo rpm --import 'https://packages.doppler.com/public/cli/gpg.DE2A7741A397C129.key'65 curl -sLf --retry 3 --tlsv1.2 --proto "=https" \66 'https://packages.doppler.com/public/cli/config.rpm.txt' \67 | sudo tee /etc/yum.repos.d/doppler-cli.repo68 sudo dnf install -y doppler || sudo yum install -y doppler69 ```7071- Linux/macOS fallback or CI — official install script:72 ```bash73 (curl -Ls --tlsv1.2 --proto "=https" --retry 3 https://cli.doppler.com/install.sh \74 || wget -t 3 -qO- https://cli.doppler.com/install.sh) | sh75 ```7677- Windows (PowerShell, winget):78 ```powershell79 winget install doppler80 ```8182- Docker / containers (no package manager): bake into the image — see `templates/Dockerfile.doppler.snippet`.8384Authoritative sources (consult only if you suspect a stale flag):85- https://docs.doppler.com/docs/install-cli86- https://github.com/DopplerHQ/cli8788After install, verify:89```bash90doppler --version91```92Do NOT proceed if the CLI is still missing — report the install failure and stop.93</step_0_install_hedge>9495<quick_start>961. Ensure CLI installed (see step 0).972. `doppler login` (once per machine; opens browser).983. In repo root: `doppler setup` to pick project + config (or commit a `doppler.yaml`).994. Run code via `doppler run -- <command>`.1005. For non-interactive environments (CI, prod, containers): use a service token via `DOPPLER_TOKEN`.101</quick_start>102103────────────────────────────────────────────────────────104PART A — AD HOC SCRIPTS105────────────────────────────────────────────────────────106107<ad_hoc_workflow>108109<step_1_preflight>110Verify CLI + auth:111```bash112command -v doppler >/dev/null || { echo "doppler CLI missing" >&2; exit 1; }113doppler configure debug >/dev/null 2>&1 || { echo "doppler not authed/configured" >&2; exit 1; }114```115</step_1_preflight>116117<step_2_validate_required_names>118Validate secret names only — never values:119```bash120doppler secrets --project=<project> --config=<config> --only-names --json121```122Compare required names against this list. Do not retrieve values during validation.123</step_2_validate_required_names>124125<step_3_execute_with_env_injection>126Preferred:127```bash128doppler run --project=<project> --config=<config> -- python /path/to/script.py129```130Multi-command:131```bash132doppler run --project=<p> --config=<c> --command="./configure && ./run; ./cleanup"133```134</step_3_execute_with_env_injection>135136<step_4_safe_python_pattern>137See `templates/secure_python_with_doppler_cli_env.py`. Read env, never print secrets:138```python139import os, sys140required = ["X_OAUTH2_ACCESS_TOKEN"]141missing = [k for k in required if not os.getenv(k)]142if missing:143 print(f"Missing required secret env vars: {', '.join(missing)}", file=sys.stderr)144 raise SystemExit(1)145token = os.environ["X_OAUTH2_ACCESS_TOKEN"] # use directly; never log146```147</step_4_safe_python_pattern>148149<step_5_output_sanitization>150Allowed: counts, ids, timestamps, non-sensitive identifiers, summarized findings.151Forbidden: token strings, secret values, env dumps, full credentialed request dumps.152</step_5_output_sanitization>153154</ad_hoc_workflow>155156────────────────────────────────────────────────────────157PART B — PROJECTS / LONG-LIVED APPLICATIONS158────────────────────────────────────────────────────────159160<project_workflow>161162<step_1_bind_repo_to_project>163Run once per repo, in repo root:164```bash165doppler setup166```167This writes scope into `~/.doppler/.doppler.yaml` (per-user, never hand-edit).168169For team-wide reproducibility, also commit a `doppler.yaml` at the repo root (see `templates/doppler.yaml`):170```yaml171setup:172 - project: my-app173 config: dev174```175Monorepo variant:176```yaml177setup:178 - path: backend/179 project: my-app-api180 config: dev181 - path: frontend/182 project: my-app-web183 config: dev184```185Teammates and CI then run:186```bash187doppler setup --no-interactive188```189</step_1_bind_repo_to_project>190191<step_2_environments_and_configs>192Doppler hierarchy: `Workspace → Project → Environment (dev/stg/prd) → Config`.193- Branch configs (e.g. `dev_personal`) inherit from a root config (`dev`) and override per-developer.194- Promote secrets between environments via the dashboard (review/approval) rather than copy-paste.195- For new projects, bootstrap structure declaratively with `doppler-template.yaml` + `doppler import`. See https://docs.doppler.com/docs/project-templates.196</step_2_environments_and_configs>197198<step_3_application_runtime>199Always launch the app under `doppler run`:200```bash201doppler run -- npm start202doppler run -- python -m myapp203doppler run -- ./bin/server204```205Do not call `doppler secrets download` to materialize a `.env` for the app at runtime if `doppler run` is viable.206</step_3_application_runtime>207208<step_4_service_tokens_for_non_interactive>209Service tokens are read-only, scoped to a single project+config. Use them everywhere a human is not present (CI, prod, containers, VMs).210211Create:212```bash213# interactive setup, then mint214doppler setup215doppler configs tokens create ci-deploy --plain216217# or single-shot218doppler configs tokens create ci-deploy \219 --project my-app --config prd --plain220```221Ephemeral token (auto-expires):222```bash223DOPPLER_TOKEN=$(doppler configs tokens create job-$(date +%s) \224 --project my-app --config prd --max-age 5m --plain)225```226Revoke:227```bash228doppler configs tokens revoke -p my-app -c prd dp.st.prd.xxxx229```230231Auth precedence (highest → lowest): `--token` flag → `DOPPLER_TOKEN` env → `--project/--config` flags → directory scope in `~/.doppler/.doppler.yaml`.232</step_4_service_tokens_for_non_interactive>233234<step_5_ci_cd>235GitHub Actions example (token from repo secrets):236```yaml237- uses: dopplerhq/cli-action@v3238- env:239 DOPPLER_TOKEN: ${{ secrets.DOPPLER_TOKEN_PRD }}240 run: doppler run -- ./deploy.sh241```242Rules:243- Store the service token in the CI provider's secret store; never echo it.244- Use one token per environment (prd/stg/dev).245- Set `HISTIGNORE='export DOPPLER_TOKEN*'` in any shell that may be recorded.246</step_5_ci_cd>247248<step_6_docker>249Pattern A — bake CLI into image, inject token at runtime (recommended):250```dockerfile251# see templates/Dockerfile.doppler.snippet for the full snippet252RUN (curl -Ls --tlsv1.2 --proto "=https" --retry 3 https://cli.doppler.com/install.sh) | sh253ENTRYPOINT ["doppler", "run", "--"]254CMD ["node", "server.js"]255```256Run:257```bash258docker run -e DOPPLER_TOKEN="$DOPPLER_TOKEN" my-app:latest259```260261Pattern B — host-side injection (no Doppler in image):262```bash263doppler run -- docker compose up264```265266Never `COPY .env` into an image. Never bake `DOPPLER_TOKEN` into a layer.267</step_6_docker>268269<step_7_kubernetes>270Two supported approaches:2712721) Doppler Kubernetes Operator (recommended) — syncs Doppler configs into native `Secret` objects; pods consume via `envFrom`. Install via Helm; see https://docs.doppler.com/docs/kubernetes-operator. The Operator handles rotation; no `doppler` binary in the image.2732742) `doppler run` inside containers using `DOPPLER_TOKEN` from a K8s Secret:275```bash276kubectl create secret generic doppler-token \277 --from-literal=DOPPLER_TOKEN='dp.st.prd.xxxx'278```279```yaml280spec:281 containers:282 - name: app283 image: my-app:latest284 envFrom:285 - secretRef:286 name: doppler-token287```288289Prefer (1) for production. Use (2) when the Operator is not available.290</step_7_kubernetes>291292<step_8_local_env_hygiene>293- Do NOT commit `.env`, `.env.local`, `.env.*` (except `.env.example` containing names only).294- DO commit `doppler.yaml` (project binding) and `doppler-template.yaml` (project bootstrap).295- Add to `.gitignore`:296 ```297 .env298 .env.*299 !.env.example300 ```301- If a `.env` must be generated for a tool that cannot use `doppler run` (e.g. some IDEs):302 ```bash303 doppler secrets download --no-file --format env > .env304 ```305 Treat the file as ephemeral, ensure `.gitignore` covers it, and delete it after use.306- Pre-commit hooks: enable a secret scanner (gitleaks, trufflehog) to catch accidental commits.307</step_8_local_env_hygiene>308309<step_9_rotation_and_history>310- View change history: `doppler secrets history SECRET_NAME` (or dashboard).311- Rotate via dashboard or integration (AWS/GCP/Azure rotation connectors).312- After rotation, restart consumers (apps re-read env at process start, not in-place).313- For automated rotation triggers, configure Doppler webhooks to ping your deploy/restart endpoint.314</step_9_rotation_and_history>315316</project_workflow>317318────────────────────────────────────────────────────────319COMMON FAILURES & FIXES320────────────────────────────────────────────────────────321322<common_failures_and_fixes>323- `doppler: command not found` → run install hedge for the OS (step 0) and re-verify with `doppler --version`.324- Auth/config not initialized → `doppler login` (interactive) or set `DOPPLER_TOKEN` (non-interactive).325- "This token does not have access to requested config" → token scope mismatch; mint a new token for the correct project+config.326- Wrong secrets returned → check resolution with `doppler configure debug` and `doppler configure --scope $(pwd)`; confirm `doppler.yaml` matches the desired config.327- Secrets logged accidentally → strip debug prints, redact exceptions, never dump env or auth headers.328- Missing secrets at runtime → verify names exist in selected project/config (`doppler secrets --only-names`) and confirm the runtime is actually under `doppler run` or has `DOPPLER_TOKEN` set.329- Token leaked in shell history → rotate the token immediately (`doppler configs tokens revoke`), mint a new one, and set `HISTIGNORE` going forward.330</common_failures_and_fixes>331332────────────────────────────────────────────────────────333VERIFICATION CHECKLIST334────────────────────────────────────────────────────────335336<verification_checklist>337- [ ] `doppler --version` succeeds (CLI installed)338- [ ] Auth confirmed (`doppler configure debug` shows token source) OR `DOPPLER_TOKEN` set339- [ ] For projects: `doppler.yaml` committed; `.env*` ignored; `.env.example` (names only) optional340- [ ] Secret names validated without reading values341- [ ] Application launched via `doppler run` (or Operator-injected env)342- [ ] No secret output in logs/messages/files/shell history343- [ ] Service tokens scoped per environment; no human tokens in CI/prod344- [ ] Final response sanitized345</verification_checklist>346347<success_criteria>348Agents reliably install, configure, and operate Doppler — for one-off scripts and long-lived projects — using CLI env injection and scoped service tokens, while preserving strict non-disclosure of secret values across all outputs.349</success_criteria>