Use Azure Database for PostgreSQL Flexible Server With Django

Connect Django to Azure Database for PostgreSQL Flexible Server: choose public or private access, require TLS, use Microsoft Entra authentication with managed identity, enable built-in PgBouncer, allow extensions, and configure high availability and backups.

Problem statement

Your Django app runs on Azure App Service, Container Apps, AKS, or a VM, and you want managed PostgreSQL. Azure Database for PostgreSQL Flexible Server is the current offering (Single Server was retired). Connecting Django works like any PostgreSQL, but Azure adds decisions: public access with firewall rules or private access in a virtual network, TLS enforcement, password versus Microsoft Entra authentication, the built-in PgBouncer, and an allowlist for extensions.

Django on Azure PostgreSQLApp on AzureApp Service, ACAPrivate accessVNet or endpointFlexible ServerTLS requiredEntra or passwordmanaged identityNetwork path, TLS, and identity are the Azure-specific parts of the setup.
  1. Create a Flexible Server in the same region as the app, preferably with private access (VNet integration).
  2. Keep require_secure_transport on and use sslmode=require in Django.
  3. Authenticate with a password from Key Vault, or with Microsoft Entra tokens from a managed identity.
  4. Enable the built-in PgBouncer on port 6432 when many app instances connect.
  5. Add needed extensions (pg_trgm, unaccent, PostGIS) to azure.extensions before migrations create them.

Step-by-step solution

1. Create the server

az postgres flexible-server create \
  --resource-group shop-prod --name shop-pg --location westeurope \
  --tier GeneralPurpose --sku-name Standard_D2ds_v5 --storage-size 64 \
  --version 16 --high-availability ZoneRedundant \
  --vnet shop-vnet --subnet pg-subnet --private-dns-zone shop-pg.private.postgres.database.azure.com
az postgres flexible-server db create -g shop-prod -s shop-pg -d shop

Private access places the server in a delegated subnet with a private DNS zone. App Service and Container Apps reach it through VNet integration. Public access with firewall rules is simpler but exposes the endpoint to the internet.

2. Connect Django with TLS

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": "shop",
        "USER": "shopapp",
        "PASSWORD": os.environ["DB_PASSWORD"],
        "HOST": "shop-pg.postgres.database.azure.com",
        "PORT": "5432",
        "OPTIONS": {"sslmode": "require"},
        "CONN_MAX_AGE": 60,
        "CONN_HEALTH_CHECKS": True,
    }
}

Create a dedicated role for the app instead of using the server admin account, and grant it ownership of the database objects it manages.

network optionsPublic + firewall rules✓quick to set up✕endpoint on the internet✕IP rules to maintainPrivate access (VNet)✓no public endpoint✓app joins via VNet✕more network setupProduction databases belong on private networksdjango-deployment.com
The network mode is chosen at creation and cannot be switched later for the same server.

3. Use Microsoft Entra authentication

With Entra authentication enabled and a role mapped to the app's managed identity, the app requests an access token and uses it as the password. Tokens expire after about an hour, so fetch a fresh one when Django opens a connection:

from azure.identity import DefaultAzureCredential
from django.db.backends.postgresql import base

_credential = DefaultAzureCredential()

class DatabaseWrapper(base.DatabaseWrapper):
    def get_connection_params(self):
        params = super().get_connection_params()
        token = _credential.get_token("https://ossrdbms-aad.database.windows.net/.default")
        params["password"] = token.token
        return params

Point ENGINE at the module containing this wrapper. Keep CONN_MAX_AGE below the token lifetime. This removes database passwords from configuration entirely.

4. Enable PgBouncer

Flexible Server includes PgBouncer, enabled through server parameters (pgbouncer.enabled = true). Connect Django to port 6432 instead of 5432. In transaction pooling mode, avoid session-level features (SET without LOCAL, advisory locks held across transactions), and keep server-side cursors off with DISABLE_SERVER_SIDE_CURSORS = True.

5. Extensions, HA, and backups

Extensions must be allowlisted in the azure.extensions server parameter before CREATE EXTENSION works, even for the admin. Zone-redundant high availability keeps a standby in another zone and fails over automatically; Django sees dropped connections for about a minute. Backups with point-in-time restore are automatic; set retention to match your recovery requirements and test restores into a new server.

server parameters and optionsrequire_secure_transport onsslmode=requireazure.extensions allowlistpg_trgm, postgispgbouncer.enabledport 6432Zone-redundant HAautomatic failoverBackup retention settest a restoreSeveral Django needs map to Azure server parametersdjango-deployment.com
Parameter changes marked static need a server restart.

Explanation

Flexible Server is PostgreSQL managed by Azure, with configuration exposed as server parameters rather than postgresql.conf access. Most Django concerns map onto those parameters: TLS enforcement, extensions, connection pooling, and timeouts. Microsoft Entra integration lets Azure-hosted apps authenticate with managed identities, aligning the database with how they already access Key Vault and Storage.

Edge cases / notes

  • Single Server. If you still run the retired Single Server, migrate to Flexible Server; connection strings and usernames differ (user@server suffix is gone).
  • Connection limits. Max connections depend on the SKU; use PgBouncer or reduce workers when many instances connect.
  • Burstable tier. B-series SKUs are cheap but throttle CPU under sustained load; use General Purpose for production traffic.

Next steps

FAQ

Should I use public or private access?

Private access for production. It keeps the database off the internet.

Can Django use a managed identity instead of a password?

Yes, with Microsoft Entra authentication and a small database wrapper that fetches tokens.

Why does CREATE EXTENSION fail even as admin?

The extension is not in the azure.extensions allowlist. Add it in the server parameters first.

Does Flexible Server include connection pooling?

Yes, a built-in PgBouncer you can enable and reach on port 6432.

Keep going