Architecture¶
End-to-end model from Pulumi program to a running App Service instance.
flowchart TD
subgraph consumer [Consumer projects]
PulumiStack[Pulumi stack]
DjangoApp[Django app repo]
end
subgraph azure [Azure resources]
SA[Storage + CDN]
PG[Postgres Flexible Server]
ASP[App Service Plan]
WA[Django Web App]
BF[Principal bootstrap Function]
PGA[pgAdmin Web App]
KV[Key Vault]
Redis[Redis sidecar]
end
subgraph deploy [Deploy on App Service]
Boot[bootstrap.django-azu.re]
Oryx[Oryx build]
Start[startup.sh]
end
PulumiStack -->|DjangoDeployment| azure
DjangoApp -->|Git source control| WA
WA --> Boot --> Oryx --> Start
Start --> WA
BF -->|create Entra principal| PG
WA --> PG
WA --> SA
WA --> KV
WA --> Redis
Shared vs per-app¶
One DjangoDeployment creates the shared plane (storage, CDN, Postgres server, plan, principal bootstrap Function, pgAdmin). Each add_django_website(...) adds an application plane (database, containers, vault, Web App, optional ACS/Redis, automatic Entra DB principal).
Runtime trust model¶
- Django Web App application settings are a separate ARM resource (
WebAppApplicationSettings), not nested inWebApp.siteConfig. UpdatingsiteConfigrecycles the worker. - Web App uses a system-assigned managed identity.
- A separate user-assigned bootstrap identity is an Entra Postgres admin used only by the shared Function to create non-admin app roles.
- Postgres uses Entra ID auth only (no password auth on the server). Django uses the
pulumi_django_azure.db.backends.postgresqlengine, which fetches a short-lived Entra access token for every new connection, in whichever process opens it (Gunicorn worker, RQ worker, management command). - Secrets from Pulumi config land in Key Vault; the app receives
{ENV}_SECRET_NAMEand reads values withAZURE_KEY_VAULT_CLIENT. - Static/media go to blob storage and are served through the CDN host.
Build and start¶
- App setting
PRE_BUILD_COMMANDcurlshttps://bootstrap.django-azu.re. - Bootstrap fills
cicd//utility/and runspre_build.sh. - Oryx installs from exported
requirements.txt;POST_BUILD_COMMANDrunspost_build.sh. - App Service starts
cicd/startup.sh(startup_tasks= migrate + purge_cache, background collectstatic, supervisord, Gunicorn). Startup time matters: when the platform moves the app to another instance it kills the old container a few minutes after starting the new one, whether or not the new one is warm yet.
Details: Deploy pipeline.