Skip to main content

Certification track: Professional Cloud Developer (PCD)

Django Common — Shared Application Configuration

Django_Common is the shared application layer for Django. It is not deployed on its own; instead it supplies the Django-specific configuration that both Django_GKE and Django_CloudRun build on, so the two platform variants behave identically where it matters. End users never configure this layer directly — it has no deployment UI inputs of its own — but understanding what it provides explains the defaults you see in the platform docs.

For the infrastructure that actually provisions and runs Django, see the platform guides (Django_GKE, Django_CloudRun) and the foundation guides (App_GKE, App_CloudRun).


1. What this layer provides

AreaProvided by Django_CommonWhere it surfaces
Django SECRET_KEYGenerates a 50-character random key and stores it in Secret ManagerInjected as SECRET_KEY at runtime; retrieve via Secret Manager (see below)
Container imagePins the Django/Gunicorn image and Cloud Build source, UID 2000container_image output of the platform deployment
Database engineFixes Cloud SQL for PostgreSQL 15 as the only supported engine§Database in the platform guides
Database bootstrapDefines the db-init job that creates the database, user, and installs extensionsinitialization_jobs output
Schema migrationsDefines the db-migrate job that runs manage.py migrate + collectstaticRuns automatically on every deploy
PostgreSQL extensionsAuto-installs pg_trgm, unaccent, hstore, citextNo user action required
Object storageDeclares the Cloud Storage media bucketstorage_buckets output
Core settingsSets container port (8080), image source (custom), extension flag, Gunicorn serverApplication behaviour in the platform guides
Health probesForwards startup_probe/liveness_probe unchanged (defaults null here — the CloudRun variant defaults to /healthz, the GKE variant defaults to /)§Observability in the platform guides

2. Django SECRET_KEY in Secret Manager

The Django SECRET_KEY is generated automatically and stored as a Secret Manager secret — it is never set in plain text. Retrieve it after deployment:

# The secret name follows the deployment's resource prefix; list and read it:
gcloud secrets list --project "$PROJECT" --filter="name~key"
gcloud secrets versions access latest --secret=<resource-prefix>-django-key --project "$PROJECT"

The database password is generated and managed separately by the foundation; its secret name is reported in the platform deployment outputs (database_password_secret).


3. Database engine and bootstrap

Django requires PostgreSQL 15; the engine is fixed and MySQL is not supported through this module. On the first deployment two jobs run in sequence:

  1. db-init (image: postgres:15-alpine) — connects to Cloud SQL through the Auth Proxy and idempotently creates the application database and user, grants privileges, and installs the four required PostgreSQL extensions:

    ExtensionPurpose
    pg_trgmTrigram similarity for full-text search
    unaccentAccent-insensitive text search
    hstoreKey-value store column type
    citextCase-insensitive text column type
  2. db-migrate (application image) — runs manage.py migrate to apply all pending migrations and manage.py collectstatic --noinput --clear to collect static files to the configured location.

Both jobs run with execute_on_apply = true so they fire automatically on each deployment. They are idempotent and safe to re-run. Inspect or re-trigger the database with:

# Cloud Run:
gcloud run jobs list --region "$REGION" --project "$PROJECT"
gcloud run jobs execute db-init-<resource-prefix> --region "$REGION" --project "$PROJECT"
# GKE:
kubectl get jobs -n "$NAMESPACE"
kubectl describe job db-init -n "$NAMESPACE"

The instance, database, and user names are in the platform deployment outputs.


4. Core application settings

Django_Common establishes the baseline Django environment so the application comes up correctly on first boot:

  • Container port 8080. Gunicorn binds to port 8080. The foundation configures the Cloud Run service or Kubernetes Service to target this port.
  • Custom image via Cloud Build. container_image_source = "custom" instructs the foundation to trigger a Cloud Build from the Django_Common/scripts/ Dockerfile, which produces a Gunicorn-based Django image tagged with application_version.
  • PostgreSQL extensions flag. enable_postgres_extensions = true is set internally so the foundation provisions the extensions IAM grants. The four extensions are installed by db-init.sh; you do not need to set this flag manually.
  • Superuser creation. entrypoint.sh looks for DJANGO_SUPERUSER_USERNAME and DJANGO_SUPERUSER_PASSWORD on startup (if DJANGO_SUPERUSER_PASSWORD is unset but DB_PASSWORD is present, it defaults the superuser password to DB_PASSWORD) and creates the account via a manage.py shell script that calls User.objects.create_superuser(...) directly — not manage.py createsuperuser --noinput. DJANGO_SUPERUSER_EMAIL is optional and defaults to admin@example.com. Use secret_environment_variables in the platform module to supply DJANGO_SUPERUSER_PASSWORD from Secret Manager.
  • GCS media storage is opt-in, not automatic. Django_Common provisions the media storage bucket and bakes django-storages[google] into the image (settings.py switches the default file storage backend to storages.backends.gcloud.GoogleCloudStorage only when the GS_BUCKET_NAME env var is set — otherwise Django falls back to local FileSystemStorage for media and Whitenoise for static files). Neither GS_BUCKET_NAME nor a matching gcs_volumes entry is set by default, so the db-migrate job's mount_gcs_volumes = ["django-media"] reference only takes effect if the platform module's gcs_volumes variable is also populated with an entry named django-media pointing at the bucket. To use GCS for media, set GS_BUCKET_NAME via environment_variables (and, if the mount is needed, add the matching gcs_volumes entry) rather than assuming it is wired up out of the box.

Platform-specific defaults (set by the CloudRun/GKE variant, not by Django_Common itself):

  • GKE defaults session_affinity = "ClientIP" so that a given user's requests are routed to the same pod when in-process session storage is used.
  • Cloud Run defaults execution_environment = "gen2" (required for NFS/GCS Fuse mounts among other gen2-only features). This is simply the module's static default — it is not toggled automatically based on enable_nfs.

5. Health probe behaviour

Django_Common itself does not set a probe default (startup_probe/liveness_probe default to null); the values below come from the CloudRun and GKE variant variables.tf defaults, both on port 8080. Cloud Run defaults its probe path to /healthz (served by the sample project's myproject/urls.py, which returns HTTP 200 once the application is ready). GKE's default probe path is / (the application root), not /healthz — the two variants diverge here, so verify the deployed path in the platform module's variables.tf before assuming either default.

  • GKE uses HTTP probes for both startup (90s initial delay) and liveness (60s initial delay) — in-cluster probe traffic reaches the container directly without routing through a load balancer.
  • Cloud Run also uses HTTP probes for both startup (60s initial delay) and liveness (30s initial delay). Unlike Mautic/Apache, Django's Gunicorn server does not redirect HTTP to HTTPS, so plain HTTP probes work without a TCP workaround. Ensure SECURE_SSL_REDIRECT = False in settings.py (or exempt /healthz) so the probe path is never redirected.

If your first-deploy migrations are large and the 60-second (Cloud Run) or 90-second (GKE) initial delay is insufficient, override startup_probe with a larger initial_delay_seconds in the platform module variables.


6. Object storage

A dedicated Cloud Storage media bucket (name_suffix = "media") is declared here and provisioned by the foundation, which also grants the workload service account access. The bucket is provisioned in the deployment region, but — as noted in §4 — neither the GCS bucket nor the shared Filestore (NFS) volume is wired into Django's MEDIA_ROOT by default: GCS needs GS_BUCKET_NAME set explicitly, and NFS's default mount path (/mnt/nfs) does not match the image's /app/media directory, so nfs_mount_path must be set to /app/media for NFS-backed media persistence. List and inspect the bucket with:

gcloud storage buckets list --project "$PROJECT"
gcloud storage ls gs://<resource-prefix>-media/

For the Django-specific, user-facing configuration (variables by group, outputs, and how to explore each service from the Console and CLI), see the platform guides: Django_GKE and Django_CloudRun.