Manage Django Secrets With Doppler

Manage Django environment variables and secrets with Doppler: organize projects and configs per environment, inject secrets with doppler run locally and on servers, sync to Heroku, Render, Vercel, or Kubernetes, use service tokens in CI, and rotate safely.

Problem statement

Your Django project's secrets live in several places: .env files on laptops, variables in the hosting dashboard, CI secrets, and a shared password manager entry that nobody trusts is current. Adding a new variable means updating all of them. Doppler is a hosted secrets manager that stores variables per project and environment and injects them into processes, platforms, and CI, so Django keeps reading os.environ while the values come from one place.

Doppler for DjangoDoppler projectdev, stg, prdInjectdoppler runSyncplatforms, k8sDjango readsos.environSettings code stays the same; Doppler supplies the environment.
  1. Create a Doppler project with configs for dev, stg, and prd, and add SECRET_KEY, DATABASE_URL, and the rest.
  2. Locally, run Django with doppler run -- python manage.py runserver; no .env file needed.
  3. On servers, install the CLI and start Gunicorn with doppler run -- gunicorn ..., authenticated by a service token.
  4. On PaaS hosts and Kubernetes, use Doppler's integrations to sync secrets into the platform's native variables or Secrets.
  5. Give CI a read-only service token for the config it needs, and rotate tokens and secrets on schedule.

Step-by-step solution

1. Organize projects and configs

A Doppler project usually maps to one Django app. Each environment is a config: dev for local work (developers can create personal branch configs from it), stg for staging, and prd for production. Inherit common values and override per config. Settings stay standard:

SECRET_KEY = os.environ["SECRET_KEY"]
DATABASES = {"default": dj_database_url.config(conn_max_age=60)}

2. Use it locally

doppler login
doppler setup          # choose project and dev config for this directory
doppler run -- python manage.py runserver
doppler run -- python manage.py migrate

doppler run fetches the config and starts the command with those variables. Developers no longer need a shared .env file, and access can be revoked per person.

environment and methodMethodCredentialLaptopdoppler runpersonal loginVM with systemddoppler run in unitservice tokenHeroku, Render, Vercelintegration syncDoppler sideKubernetesoperator or syncservice tokenCI pipelineCLI or actionread-only tokenOne source, delivered through each platform’s native mechanismdjango-deployment.com
Service tokens are scoped to one config, so a leaked token exposes one environment.

3. Run on servers

Create a service token for the prd config and store it on the server as the only secret:

# /etc/systemd/system/shop.service
[Service]
User=deploy
Environment=DOPPLER_TOKEN=dp.st.prd.xxxx
ExecStart=/usr/bin/doppler run -- /srv/app/.venv/bin/gunicorn config.wsgi --bind unix:/run/shop/gunicorn.sock

Better, put the token in a root-readable EnvironmentFile. The CLI can cache the fetched secrets encrypted on disk with a fallback file, so a Doppler outage does not stop restarts.

4. Sync to platforms

Doppler integrations push secrets into Heroku config vars, Render environment groups, Vercel environment variables, AWS Secrets Manager or Parameter Store, GitHub Actions secrets, and others. Changes in Doppler update the platform, which may trigger a redeploy depending on the platform. For Kubernetes, the Doppler operator syncs a config into a Kubernetes Secret, and can restart Deployments when it changes.

5. Rotate and audit

Doppler keeps a history of changes and an audit log. When rotating SECRET_KEY, use Django's SECRET_KEY_FALLBACKS so sessions survive:

SECRET_KEY = os.environ["SECRET_KEY"]
SECRET_KEY_FALLBACKS = [k for k in os.environ.get("SECRET_KEY_FALLBACKS", "").split(",") if k]

Update the values in Doppler, let them sync, restart processes, and remove the fallback after sessions expire.

adoption steps1Import existing .env valuesper environment2Developers use doppler rundelete local .env3Servers get service tokensone per config4Sync to platforms and CIintegrations5Rotate leaked-prone secretsaudit logSettings code does not change; only where values come fromdjango-deployment.com
Rotate anything that lived in old .env files shared over chat.

Explanation

Doppler stores secrets centrally with access control per project and config, and delivers them as environment variables at process start or by syncing into other systems. Because Django already reads configuration from os.environ in twelve-factor style setups, adopting Doppler does not require code changes. The benefits are fewer copies of each secret, revocable access, change history, and consistent values across local, CI, and production.

Edge cases / notes

  • Process restarts. Environment variables are read at start; processes need a restart (or a redeploy) to see changed values.
  • Build-time variables. Values needed during collectstatic or Docker builds must be passed into the build explicitly, ideally only non-secret ones.
  • Outages. Use the CLI's fallback file or platform syncs so a temporary Doppler outage does not block restarts.

Next steps

FAQ

Does Django need a Doppler library?

No. Doppler injects environment variables; Django reads them with os.environ as usual.

What happens if Doppler is down?

doppler run can use an encrypted fallback file, and platform syncs keep the last values. Configure one of them.

How do servers authenticate to Doppler?

With service tokens scoped to a single config, stored as the only secret on the server.

Can Doppler sync to Kubernetes?

Yes, through the Doppler Kubernetes operator, which keeps a Secret in sync.

Keep going