Skip to content

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]
    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
  WA --> PG
  WA --> SA
  WA --> KV
  WA --> Redis

Shared vs per-app

One DjangoDeployment creates the shared plane (storage, CDN, Postgres server, plan, pgAdmin). Each add_django_website(...) adds an application plane (database, containers, vault, Web App, optional ACS/Redis).

See Multiple applications.

Runtime trust model

  • Web App uses a system-assigned managed identity.
  • Postgres uses Entra ID auth only (no password auth on the server). The app password in Django is a short-lived Entra access token refreshed via health checks.
  • Secrets from Pulumi config land in Key Vault; the app receives {ENV}_SECRET_NAME and reads values with AZURE_KEY_VAULT_CLIENT.
  • Static/media go to blob storage and are served through the CDN host.

Build and start

  1. App setting PRE_BUILD_COMMAND curls https://bootstrap.django-azu.re.
  2. Bootstrap fills cicd/ / utility/ and runs pre_build.sh.
  3. Oryx installs from exported requirements.txt; POST_BUILD_COMMAND runs post_build.sh.
  4. App Service starts cicd/startup.sh (migrate, collectstatic, supervisord, Gunicorn).

Details: Deploy pipeline.