Edit

Deploy a Django app with SQL Server to Azure App Service

This article explains how to deploy a Django application that uses the mssql-django backend to Azure App Service, including ODBC driver configuration, environment-based secrets, and managed identity authentication.

Prerequisites

  • An Azure subscription
  • An Azure SQL Database or SQL Server instance accessible from Azure
  • A Django project configured with mssql-django
  • Azure CLI installed

ODBC driver on Azure App Service

Azure App Service Linux instances include the Microsoft ODBC Driver for SQL Server. You can verify the installed driver version by running:

az webapp ssh --resource-group <your-rg> --name <your-app>
odbcinst -j

Note

Azure App Service typically includes ODBC Driver 17 and/or 18 pre-installed on Linux plans. Windows App Service plans also include the ODBC driver.

Use environment variables for secrets

Don't hardcode database credentials in settings.py. Use environment variables and configure them as App Service application settings:

import os

DATABASES = {
    "default": {
        "ENGINE": "mssql",
        "NAME": os.environ.get("DB_NAME", "<your-database>"),
        "USER": os.environ.get("DB_USER", ""),
        "PASSWORD": os.environ.get("DB_PASSWORD", ""),
        "HOST": os.environ.get("DB_HOST", "<your-server>.database.windows.net"),
        "PORT": os.environ.get("DB_PORT", "1433"),
        "OPTIONS": {
            "driver": "ODBC Driver 18 for SQL Server",
        },
    },
}

Tip

For required values like DB_NAME and DB_HOST, consider using os.environ["DB_NAME"] (without a default) so the application fails immediately with a clear KeyError if the environment variable is missing.

Set the environment variables in Azure App Service:

az webapp config appsettings set \
    --resource-group <your-rg> \
    --name <your-app> \
    --settings DB_NAME=<your-database> DB_HOST=<your-server>.database.windows.net DB_USER=<your-username> DB_PASSWORD=<your-password>

Use managed identity authentication

For production deployments, use managed identity to avoid storing credentials. Enable system-assigned managed identity on your App Service:

az webapp identity assign --resource-group <your-rg> --name <your-app>

Grant the managed identity access to your Azure SQL database:

CREATE USER [<your-app-name>] FOR EXTERNAL PROVIDER;

ALTER ROLE db_datareader ADD MEMBER [<your-app-name>];
ALTER ROLE db_datawriter ADD MEMBER [<your-app-name>];
ALTER ROLE db_ddladmin ADD MEMBER [<your-app-name>];

Note

Grant only the roles your application needs. The db_ddladmin fixed database role is required only if the application runs migrations. For read-only workloads, db_datareader is sufficient.

If your server is configured for Microsoft Entra-only authentication, FROM EXTERNAL PROVIDER fails with Msg 33130 because the server can't reach Microsoft Graph to resolve the identity name. Create the user manually with CREATE USER [<your-app-name>] WITH SID = 0x<sid-hex>, TYPE = E, where <sid-hex> is derived from the managed identity's application (client) ID, not its object ID. For the conversion steps, see Grant the identity access in Azure SQL.

Configure settings.py to use managed identity:

import os

DATABASES = {
    "default": {
        "ENGINE": "mssql",
        "NAME": os.environ.get("DB_NAME", "<your-database>"),
        "HOST": os.environ.get("DB_HOST", "<your-server>.database.windows.net"),
        "PORT": "1433",
        "OPTIONS": {
            "driver": "ODBC Driver 18 for SQL Server",
            "extra_params": "Authentication=ActiveDirectoryMsi",
        },
    },
}

Use access tokens with ManagedIdentityCredential

Alternatively, use the TOKEN setting with azure.identity:

Caution

The TOKEN value is fetched once at process startup and expires after 60-90 minutes. For long-running App Service deployments, use this approach only if you also implement token refresh logic or short-lived worker recycling. If the direct ActiveDirectoryMsi pattern works in your environment, it avoids the startup-time token issue.

import os
from azure.identity import ManagedIdentityCredential

credential = ManagedIdentityCredential()
token = credential.get_token("https://database.windows.net/.default").token

DATABASES = {
    "default": {
        "ENGINE": "mssql",
        "NAME": os.environ.get("DB_NAME", "<your-database>"),
        "HOST": os.environ.get("DB_HOST", "<your-server>.database.windows.net"),
        "PORT": "1433",
        "TOKEN": token,
        "OPTIONS": {
            "driver": "ODBC Driver 18 for SQL Server",
        },
    },
}

Tip

DefaultAzureCredential is convenient for development and shared codebases because it tries multiple credential types automatically, so the same code works on a laptop, in CI, and in Azure. However, each credential type that doesn't apply adds several seconds of timeout, which slows down startup. Since App Service always has managed identity available, ManagedIdentityCredential authenticates immediately without the probe chain. Install azure-identity in your requirements file: pip install azure-identity.

Run migrations during deployment

Add a post-deployment script or startup command to run migrations automatically:

az webapp config set \
    --resource-group <your-rg> \
    --name <your-app> \
    --startup-file "python manage.py migrate && gunicorn myproject.wsgi"

Collect static files

Configure static file handling for production:

STATIC_URL = "/static/"
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")

Run collectstatic as part of your deployment:

python manage.py collectstatic --noinput

Deploy to App Service

App Service can host a Django app in two ways:

  • Built-in Linux Python image: App Service builds your code from source and provides the Python runtime.
  • Custom Docker image: you build the image yourself and reference it from a container registry.

Both paths can use a system-assigned managed identity to authenticate to Azure SQL, but the recipe differs. Pick the tab that matches your deployment.

With the built-in Linux Python image, App Service's Oryx builder installs requirements.txt and starts your Django app under gunicorn automatically. The local managed-identity HTTP proxy makes Authentication=ActiveDirectoryMsi work directly from the ODBC connection string. Use the settings.py shown in Use managed identity authentication.

Deploy your code with az webapp up, enable system-assigned managed identity, and set the database environment variables:

az webapp up \
    --resource-group <your-rg> \
    --name <your-app> \
    --runtime "PYTHON:3.12" \
    --sku B1

az webapp identity assign --resource-group <your-rg> --name <your-app>

az webapp config appsettings set \
    --resource-group <your-rg> \
    --name <your-app> \
    --settings DB_NAME=<your-database> DB_HOST=<your-server>.database.windows.net

Then grant the managed identity access in SQL as described in Use managed identity authentication.

Local development with Docker Compose

For local development and testing, use Docker Compose to run your Django app alongside a SQL Server container:

# docker-compose.yml
services:
  db:
    image: mcr.microsoft.com/mssql/server:2022-latest
    environment:
      ACCEPT_EULA: "Y"
      MSSQL_SA_PASSWORD: "<password>"  # Must meet SQL Server complexity requirements
    ports:
      - "1433:1433"

  web:
    build: .
    ports:
      - "8000:8000"
    environment:
      DB_HOST: db
      DB_NAME: mydb
      DB_USER: sa
      DB_PASSWORD: "<password>"
    depends_on:
      - db

Tip

The SQL Server container doesn't create application databases automatically. After starting the containers, create the database and run migrations:

docker compose exec db /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "<password>" -No -Q "CREATE DATABASE mydb"
docker compose exec web python manage.py migrate

The SQL Server container image requires ACCEPT_EULA=Y and a strong SA password. For production environments, use Azure SQL Database with managed identity instead of SQL Server credentials. See Use managed identity authentication.