Edit

Container and local development with mssql-python

This guide covers environment setup for Python developers working with the mssql-python driver across Windows, Linux, macOS, Docker containers, devcontainers, and CI pipelines.

Prerequisites

  • Python 3.10 or later.
  • Docker Desktop (for container-based development).
  • An x64-compatible host (Intel, AMD, or x64 VM) for SQL Server Linux containers. SQL Server Linux containers don't support ARM64 hosts.

The go-sqlcmd utility can create a SQL Server container in a single command. It handles the Docker image pull, password generation, port assignment, and connection context automatically:

sqlcmd create mssql --accept-eula

To create a container with a sample database already attached:

sqlcmd create mssql --accept-eula --using https://aka.ms/AdventureWorksLT.bak

After creation, sqlcmd stores the connection context so you can query immediately:

sqlcmd query "SELECT @@VERSION"

Create an application login once, then use it in your Python code:

sqlcmd query --database <database> "CREATE LOGIN <app-login> WITH PASSWORD = '<password>';"
sqlcmd query --database <database> "CREATE USER <app-login> FOR LOGIN <app-login>;"
sqlcmd query --database <database> "ALTER ROLE db_datareader ADD MEMBER <app-login>;"
sqlcmd query --database <database> "ALTER ROLE db_datawriter ADD MEMBER <app-login>;"

Replace <database>, <app-login>, and <password> with values from your environment.

Connect from Python using the connection details that sqlcmd printed at creation. Use sqlcmd config view to retrieve them later:

import mssql_python

conn = mssql_python.connect(
    server="localhost,1433",
    uid="<app login>",
    pwd="<password>",
    encrypt="yes",
    trust_server_certificate="yes"
)

cursor = conn.cursor()
cursor.execute("SELECT @@VERSION")
print(cursor.fetchval())
conn.close()

When you're done, stop or delete the container:

sqlcmd stop
sqlcmd delete

Tip

Run sqlcmd create mssql --user-database <database> to create a container with an empty user database ready for development.

Local SQL Server from VS Code

The SQL Server extension for VS Code (ms-mssql.mssql) can create local SQL Server containers directly from the editor:

  1. Open the SQL Server view in the Activity Bar.
  2. Select Add Connection > Create Local SQL Server (or use the Command Palette: MS SQL: Create Local SQL Server).
  3. Choose the SQL Server version and accept the EULA.
  4. The extension pulls the container image, generates a password, and adds a connection profile automatically.

Once the container is running, you can browse databases, run queries, and manage objects right in VS Code before switching to Python code.

Local SQL Server with Docker

If you prefer to manage containers directly, the official SQL Server container image works with two environment variables:

docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=YourStr0ngP@ssword" \
  -p 1433:1433 --name sql1 \
  -d mcr.microsoft.com/mssql/server:2022-latest

Wait a few seconds, then connect from Python:

import mssql_python

conn = mssql_python.connect(
    server="localhost,1433",
    uid="<app login>",
    pwd="<password>",
    encrypt="yes",
    trust_server_certificate="yes"
)

cursor = conn.cursor()
cursor.execute("SELECT @@VERSION")
print(cursor.fetchval())
conn.close()

Important

Use MSSQL_SA_PASSWORD for SQL Server containers. The older SA_PASSWORD variable is deprecated. The password must meet SQL Server complexity requirements: at least 8 characters, with uppercase, lowercase, digits, and special characters.

To load the AdventureWorks sample database into the container:

# Download AdventureWorks backup
curl -L -o AdventureWorks2022.bak \
  "https://github.com/Microsoft/sql-server-samples/releases/download/adventureworks/AdventureWorks2022.bak"

# Copy into container
docker cp AdventureWorks2022.bak sql1:/var/opt/mssql/backup/

# Restore
docker exec sql1 /opt/mssql-tools18/bin/sqlcmd \
  -S localhost -U sa -P "YourStr0ngP@ssword" -C \
  -Q "RESTORE DATABASE AdventureWorks2022 FROM DISK='/var/opt/mssql/backup/AdventureWorks2022.bak' WITH MOVE 'AdventureWorks2022' TO '/var/opt/mssql/data/AdventureWorks2022.mdf', MOVE 'AdventureWorks2022_log' TO '/var/opt/mssql/data/AdventureWorks2022_log.ldf'"

Tip

The sqlcmd create mssql --using approach in the previous section handles the download and restore automatically.

Dockerfile for Python applications

Keep the Python base image reference in one place so local builds, devcontainers, and CI pipelines don't drift. For local experimentation, a broad supported tag such as python:3-slim works well. For shared devcontainers, CI, and production, replace that tag with an approved digest-pinned image from your organization's allowlist.

Create a minimal Dockerfile for a Python application that connects to Microsoft SQL:

ARG PYTHON_BASE=python:3-slim
FROM ${PYTHON_BASE}

# Install system libraries required by mssql-python on Linux
RUN apt-get update && \
    apt-get install -y --no-install-recommends libltdl7 libkrb5-3 libgssapi-krb5-2 && \
    rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .
CMD ["python", "app.py"]

Your requirements.txt:

mssql-python>=1.11.0

Build and run:

docker build -t myapp .
docker run -e SQL_SERVER=host.docker.internal,1433 myapp

In shared environments, pass an approved immutable base image reference with --build-arg PYTHON_BASE=python:3-slim@sha256:<approved-digest>.

Note

Use host.docker.internal on Docker Desktop (Windows and macOS) to reach a SQL Server on the host machine. On Linux, use --network host instead.

Alpine Linux

Alpine uses musl instead of glibc. Install the required packages:

ARG PYTHON_BASE=python:3-alpine
FROM ${PYTHON_BASE}

RUN apk add --no-cache libltdl krb5-libs

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .
CMD ["python", "app.py"]

Devcontainer setup

Reuse the same Dockerfile that your application builds with. This approach keeps the devcontainer aligned with your runtime image and prevents scattering Python version pins across multiple files.

Create a .devcontainer/devcontainer.json for VS Code:

{
    "name": "Python + SQL Server",
    "build": {
        "dockerfile": "../Dockerfile",
        "context": ".."
    },
    "features": {
        "ghcr.io/devcontainers/features/docker-in-docker:2": {}
    },
    "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
    "postCreateCommand": "pip install --no-cache-dir -r requirements.txt",
    "forwardPorts": [1433],
    "customizations": {
        "vscode": {
            "extensions": [
                "ms-python.python",
                "ms-mssql.mssql"
            ]
        }
    }
}

To include SQL Server as a service in the devcontainer, use Docker Compose:

.devcontainer/docker-compose.yml:

services:
  app:
    build:
      context: ..
      dockerfile: Dockerfile
    volumes:
      - ..:/workspace:cached
    command: sleep infinity
    depends_on:
      - db

  db:
    image: mcr.microsoft.com/mssql/server:2022-latest
    environment:
      ACCEPT_EULA: "Y"
      MSSQL_SA_PASSWORD: "YourStr0ngP@ssword"
    ports:
      - "1433:1433"

.devcontainer/devcontainer.json (Compose version):

{
    "name": "Python + SQL Server",
    "dockerComposeFile": "docker-compose.yml",
    "service": "app",
    "workspaceFolder": "/workspace",
    "postCreateCommand": "pip install -r requirements.txt",
    "customizations": {
        "vscode": {
            "extensions": [
                "ms-python.python",
                "ms-mssql.mssql"
            ]
        }
    }
}

For shared workspaces, pin the SQL Server service image to an approved digest instead of relying on a floating tag. Load MSSQL_SA_PASSWORD from a local .env file or a platform secret store instead of checking it into source control.

Connect to the SQL Server service by name:

conn = mssql_python.connect(
    server="db,1433",
    uid="<app login>",
    pwd="<password>",
    encrypt="yes",
    trust_server_certificate="yes"
)

Platform-specific dependencies

The mssql-python driver bundles its native components. You don't need to install an external ODBC driver manager. However, the driver requires a small set of system libraries on Linux and macOS.

Platform Required packages Install command
Windows None Included in the wheel.
Ubuntu / Debian libltdl7, libkrb5-3, libgssapi-krb5-2 sudo apt-get install libltdl7 libkrb5-3 libgssapi-krb5-2
Red Hat / CentOS / Fedora libtool-ltdl, krb5-libs sudo dnf install libtool-ltdl krb5-libs
Alpine libltdl, krb5-libs apk add libltdl krb5-libs
macOS OpenSSL (via Homebrew) brew install openssl

For macOS, if you encounter SSL errors, set the linker flags:

export LDFLAGS="-L/opt/homebrew/opt/openssl/lib"
export CPPFLAGS="-I/opt/homebrew/opt/openssl/include"

For complete installation instructions, see Install mssql-python.

Authentication for development

Local development against Azure SQL

Use ActiveDirectoryDefault for passwordless authentication. This option chains through Azure CLI, Visual Studio, environment variables, and managed identity automatically:

conn = mssql_python.connect(
    server="<server>.database.windows.net",
    database="<database>",
    authentication="ActiveDirectoryDefault",
    encrypt="yes"
)

Make sure you're signed in by using Azure CLI:

az login

Local development against SQL Server

Use SQL authentication with a local instance.

conn = mssql_python.connect(
    server="localhost,1433",
    uid="<app login>",
    pwd="<password>",
    encrypt="yes",
    trust_server_certificate="yes"
)

Container development against Azure SQL

For containers running in Azure (App Service, Container Apps, AKS), use managed identity.

conn = mssql_python.connect(
    server="<server>.database.windows.net",
    database="<database>",
    authentication="ActiveDirectoryMSI",
    encrypt="yes"
)

For containers running locally that need to connect to Azure SQL, make sure the container has a credential source that ActiveDirectoryDefault can use. The most reliable options are:

  • Install Azure CLI in the container and sign in there. Mount ~/.azure from the host only if the container image already includes Azure CLI and you intend to reuse that credential cache.
  • Provide service principal credentials through environment variables such as AZURE_CLIENT_ID, AZURE_TENANT_ID, and AZURE_CLIENT_SECRET.

Then use ActiveDirectoryDefault in your connection code.

Supported Microsoft SQL endpoints

The mssql-python driver connects to all Microsoft SQL endpoints:

Endpoint Authentication
SQL Server (on-premises or in a VM) SQL auth, Windows auth
Azure SQL Database Microsoft Entra ID (recommended), SQL auth
Azure SQL Managed Instance Microsoft Entra ID (recommended), SQL auth
Azure Synapse Analytics (dedicated pools) Microsoft Entra ID, SQL auth
SQL database in Fabric Microsoft Entra ID
Fabric Data Warehouse Microsoft Entra ID
SQL analytics endpoint (Lakehouse) Microsoft Entra ID
SQL analytics endpoint (mirrored database) Microsoft Entra ID

See Microsoft Entra authentication for all seven authentication modes and Support lifecycle for the complete compatibility matrix.

CI pipeline setup

GitHub Actions

Keep the Python runtime in one variable so you can review and update it in one place. Use 3.x for fast-moving validation pipelines, or replace it with an organization-approved exact version for release pipelines.

name: Test with SQL Server
on: [push, pull_request]

env:
  PYTHON_VERSION: "3.x"

jobs:
  test:
    runs-on: ubuntu-latest

    services:
      sqlserver:
        image: mcr.microsoft.com/mssql/server:2022-latest
        env:
          ACCEPT_EULA: Y
          MSSQL_SA_PASSWORD: YourStr0ngP@ssword
        ports:
          - 1433:1433
        options: >-
          --health-cmd "/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P YourStr0ngP@ssword -C -Q 'SELECT 1'"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: ${{ env.PYTHON_VERSION }}
          check-latest: true

      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y libltdl7 libkrb5-3 libgssapi-krb5-2
          pip install -r requirements.txt

      - name: Run tests
        env:
          SQL_SERVER: localhost,1433
          SQL_UID: sa
          SQL_PWD: YourStr0ngP@ssword
        run: pytest

For shared pipelines, replace the inline placeholder password with an encrypted secret, pin the SQL Server service image to a digest, and keep the Python version in an organization-managed variable or reusable workflow input.

Azure Pipelines

Use a container resource to run SQL Server as a service alongside your test job:

trigger:
  - main

variables:
  python.version: "3.x"

resources:
  containers:
    - container: sqlserver
      image: mcr.microsoft.com/mssql/server:2022-latest
      env:
        ACCEPT_EULA: Y
        MSSQL_SA_PASSWORD: YourStr0ngP@ssword
      ports:
        - 1433:1433

pool:
  vmImage: ubuntu-latest

services:
  sqlserver: sqlserver

steps:
  - task: UsePythonVersion@0
    inputs:
      versionSpec: "$(python.version)"

  - script: |
      sudo apt-get update
      sudo apt-get install -y libltdl7 libkrb5-3 libgssapi-krb5-2
      pip install -r requirements.txt
    displayName: Install dependencies

  - script: pytest
    displayName: Run tests
    env:
      SQL_SERVER: localhost,1433
      SQL_UID: sa
      SQL_PWD: YourStr0ngP@ssword

As with GitHub Actions, replace the inline placeholder password with a secret variable before you use this pattern outside a disposable demo pipeline.

Security and secrets

Don't hardcode database passwords or connection strings in source code or Dockerfiles. Use environment variables and secrets management instead.

Environment variables for local development

Store credentials in environment variables or a .env file that is excluded from source control:

# .env (add to .gitignore)
SQL_SERVER=localhost,1433
SQL_UID=sa
SQL_PWD=YourStr0ngP@ssword
import os
import mssql_python

conn = mssql_python.connect(
    server=os.environ["SQL_SERVER"],
    uid=os.environ["SQL_UID"],
    pwd=os.environ["SQL_PWD"],
    encrypt="yes",
    trust_server_certificate="yes"
)

For Docker Compose, reference a .env file:

services:
  app:
    build: .
    env_file: .env

Caution

Never commit .env files to source control. Add .env to your .gitignore file.

CI/CD secrets

In CI pipelines, use the platform's secret store instead of plaintext environment variables:

Container supply-chain hygiene

Use these practices for shared developer environments and CI:

  • Keep image references in one place, such as a Docker ARG, a devcontainer build, or a pipeline variable.
  • Pin shared container images to immutable digests instead of floating tags.
  • Review and refresh pinned digests through an approved update process such as Dependabot, Renovate, or an internal image promotion workflow.
  • Commit a dependency lock file such as uv.lock, or use hashed requirements files for reproducible Python installs.
  • Prefer organization-approved base images and internal registry mirrors when your platform provides them.

Production: passwordless authentication

For production workloads against Azure SQL, use Microsoft Entra authentication with managed identity. This approach eliminates passwords entirely:

conn = mssql_python.connect(
    server="<server>.database.windows.net",
    database="<database>",
    authentication="ActiveDirectoryMSI",
    encrypt="yes"
)

For applications that need to store secrets such as SQL auth passwords, use Azure Key Vault and retrieve them at runtime.

Dependency management with uv

uv is a fast Python package installer that works well in CI and container builds:

ARG PYTHON_BASE=python:3-slim
FROM ${PYTHON_BASE}

RUN apt-get update && \
    apt-get install -y --no-install-recommends libltdl7 libkrb5-3 libgssapi-krb5-2 && \
    rm -rf /var/lib/apt/lists/*

# Install uv. In shared builds, pin the source image to an approved digest.
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv

WORKDIR /app
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev

COPY . .
CMD ["uv", "run", "python", "app.py"]

In CI:

pip install uv
uv sync
uv run pytest

Troubleshoot common container issues

Symptom Cause Fix
ImportError: libltdl.so.7 Missing system library. Install libltdl7 (Debian) or libltdl (Alpine).
ImportError: libkrb5.so.3 Missing Kerberos library. Install libkrb5-3 (Debian) or krb5-libs (Alpine/RHEL).
SSL: CERTIFICATE_VERIFY_FAILED Self-signed cert on local SQL Server. Add trust_server_certificate="yes" to the connection. Don't use this in production.
Connection refused on port 1433 SQL Server container not ready. Add a health check or wait for the service to start.
Login failed for user 'sa' Password doesn't meet complexity requirements. Use a password with uppercase, lowercase, digits, and special characters.
Cannot open database Database doesn't exist yet. Create or restore the database before connecting.
Slow first connection in container DNS resolution or credential chain startup. For local SQL Server, use localhost,1433 instead of hostname. For Azure SQL, pre-authenticate with az login.