Use Google Secret Manager With Django
Load Django secrets from Google Cloud Secret Manager: mount secrets as environment variables or files on Cloud Run, read them with the Python client on Compute Engine and GKE, grant least-privilege access to service accounts, and rotate secrets with versions.
Problem statement
Your Django app runs on Google Cloud, and secrets such as SECRET_KEY, the database password, and API keys should not sit in plain environment variables in deployment files or in a .env baked into an image. Google Secret Manager stores secrets with versions and IAM access control. Cloud Run can expose them directly as environment variables or files, and other platforms can read them with the client library at startup.
- Create secrets:
printf %s "$VALUE" | gcloud secrets create django-secret-key --data-file=-. - Grant the runtime service account
roles/secretmanager.secretAccessoron each secret. - Cloud Run:
--set-secrets=SECRET_KEY=django-secret-key:latestexposes it as an environment variable. - Compute Engine or GKE: read secrets with
google-cloud-secret-managerin settings, or sync with External Secrets Operator. - Pin versions for predictable deploys, and rotate by adding a new version and redeploying.
Step-by-step solution
1. Create secrets
python -c "import secrets; print(secrets.token_urlsafe(50), end='')" | \
gcloud secrets create django-secret-key --replication-policy=automatic --data-file=-
printf %s "postgres://shop:...@/shop?host=/cloudsql/acme:europe-west1:shop-db" | \
gcloud secrets create django-database-url --data-file=-
Use printf %s or end='' so no trailing newline ends up in the value, a common cause of authentication failures.
2. Grant access
gcloud secrets add-iam-policy-binding django-secret-key \
--member=serviceAccount:shop-run@acme.iam.gserviceaccount.com \
--role=roles/secretmanager.secretAccessor
Grant per secret rather than project-wide, so the web service cannot read secrets meant for other services.
3. Cloud Run
gcloud run deploy shop --image=europe-west1-docker.pkg.dev/acme/app/shop:3f2a1c9 \
--service-account=shop-run@acme.iam.gserviceaccount.com \
--set-secrets=SECRET_KEY=django-secret-key:latest,DATABASE_URL=django-database-url:latest
Django reads os.environ["SECRET_KEY"] as usual. With :latest, the value is resolved when an instance starts, so a new secret version reaches new instances without a redeploy, but running instances keep the old value. Pinning a version number (:3) makes rollouts explicit. Mounting as a file (--set-secrets=/secrets/key=django-secret-key:latest) lets the file update in running instances.
4. Compute Engine and GKE
Read secrets in settings at startup:
from google.cloud import secretmanager
def gcp_secret(name, version="latest", project=os.environ.get("GOOGLE_CLOUD_PROJECT")):
client = secretmanager.SecretManagerServiceClient()
path = f"projects/{project}/secrets/{name}/versions/{version}"
return client.access_secret_version(name=path).payload.data.decode()
SECRET_KEY = os.environ.get("SECRET_KEY") or gcp_secret("django-secret-key")
Credentials come from the VM's service account or GKE workload identity. Fetching at import time adds a network call to every process start; cache values for the process lifetime. On GKE, the Secret Manager CSI driver or External Secrets Operator avoids code changes by providing files or Kubernetes Secrets.
5. Rotate secrets
Add a new version, deploy or restart to pick it up, then disable the old version:
printf %s "$NEW_VALUE" | gcloud secrets versions add django-database-url --data-file=-
gcloud secrets versions disable 2 --secret=django-database-url
For SECRET_KEY, keep the previous value in SECRET_KEY_FALLBACKS during the transition so sessions and password reset links stay valid.
Explanation
Secret Manager stores each secret as a series of immutable versions, protected by IAM and logged in Cloud Audit Logs. Platforms either resolve versions for you (Cloud Run, Cloud Build) or let your code fetch them with the identity attached to the workload. Django needs no special integration because the values end up as ordinary environment variables or strings in settings.
Edge cases / notes
- Trailing newlines. Values created with
echoinclude a newline; always useprintf %s. - Local development. Use a local
.envorgcloud auth application-default loginwith a dev project's secrets, never production ones. - Costs. Access operations are billed per 10,000 calls; fetching once per process start is negligible.
Next steps
- Deploy with secrets on Google Cloud Run.
- Compare approaches in Manage Django Secrets in Production.
- Rotate the key safely with Rotate the Django SECRET_KEY Safely.
FAQ
How does Cloud Run pass secrets to Django?
As environment variables or mounted files, configured with --set-secrets.
Do I need a service account key file?
No. Use the service account attached to Cloud Run, the VM, or the GKE workload.
Does update running instances?
Not for environment variables. New instances get the new value; mounted files can update in place.
Should I fetch secrets on every request?
No. Read them once at startup and keep them for the process lifetime.