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.
- Create a Flexible Server in the same region as the app, preferably with private access (VNet integration).
- Keep
require_secure_transporton and usesslmode=requirein Django. - Authenticate with a password from Key Vault, or with Microsoft Entra tokens from a managed identity.
- Enable the built-in PgBouncer on port 6432 when many app instances connect.
- Add needed extensions (pg_trgm, unaccent, PostGIS) to
azure.extensionsbefore 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.
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.
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@serversuffix 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
- General managed database practices are in Configure Managed Postgres for Django.
- Host the app with Deploy Django on Azure Container Apps.
- Fix extension errors with Fix Permission Denied to Create Extension.
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.