SQL Server on Linux in a Container: What Microsoft Platform Shift Means

In March 2016, Microsoft announced SQL Server on Linux. I read the press release twice to make sure I had it right. The product that had been a Windows-only server application for its entire 20+ year existence was going to run on Ubuntu and Red Hat. And shortly after: SQL Server would run in a Docker container.

This wasn't a slow evolution. It was a platform strategy pivot, and its implications for how data engineers think about SQL Server deployments were immediate.

Why This Was Surprising

SQL Server's Windows dependency wasn't just a build system choice — it was architectural. The product used Windows APIs throughout: NTFS file system integration, Windows Authentication via Active Directory, Windows Security for memory access auditing, Windows Event Log for system events. Moving to Linux required either wrapping those Windows API calls in a compatibility layer or replacing them with POSIX equivalents.

Microsoft built the Structured Query Language Application Architecture Layer (SQLPAL) — essentially a POSIX compatibility shim that let the SQL Server process run on Linux while internal code still made Windows API calls, translated transparently. This is the same approach they used for SQL Server on Docker: the container image was Linux, but the SQL Server binaries running inside it thought they were on Windows.

The result was that SQL Server on Linux (and in Docker) was functionally equivalent to SQL Server on Windows for the core engine: T-SQL, stored procedures, transactions, indexes, AlwaysOn Availability Groups. SQL Server Agent was absent from the initial Linux release. SSRS wasn't available. Some Active Directory integration scenarios required additional configuration. But the database engine itself was the same product.

What SQL Server in Docker Looked Like

# Run SQL Server 2017 in a Docker container
docker run -e 'ACCEPT_EULA=Y' \
           -e 'SA_PASSWORD=YourStr0ngPassword' \
           -p 1433:1433 \
           -v sqldata:/var/opt/mssql \
           --name sql-server-dev \
           -d mcr.microsoft.com/mssql/server:2017-latest

# Connect from SSMS or Azure Data Studio using:
# Server: localhost,1433
# Authentication: SQL Server Authentication
# Login: sa
# Password: YourStr0ngPassword

The volume mount (-v sqldata:/var/opt/mssql) was critical — it persisted the database files outside the container. Without it, the container's filesystem was ephemeral: stop the container, lose your databases. With the volume mount, data survived container restarts, upgrades, and replacements.

What This Changed for Development and Testing

The immediate practical benefit was developer experience. Before SQL Server on Docker, a developer working on a project that needed SQL Server had two options: install SQL Server locally (heavyweight, takes 20 minutes, pollutes your development machine) or connect to a shared dev SQL Server (works until someone else's schema changes break your work).

With Docker, the setup was:

# docker-compose.yml for a project that needs SQL Server
version: '3.8'
services:
  sqlserver:
    image: mcr.microsoft.com/mssql/server:2017-latest
    environment:
      ACCEPT_EULA: Y
      SA_PASSWORD: DevPassword123!
    ports:
      - "1433:1433"
    volumes:
      - sqldata:/var/opt/mssql

  app:
    build: .
    depends_on:
      - sqlserver
    environment:
      DB_CONNECTION: "Server=sqlserver,1433;Database=AppDb;User=sa;Password=DevPassword123!"

volumes:
  sqldata:

docker-compose up and you had a SQL Server instance for the project, isolated from every other project on the machine, version-pinned to the image tag, reproducible on every team member's machine. SSDT schema deployments could run against the containerized instance as part of the development workflow.

The CI Pipeline Implication

The container image meant you could run integration tests in CI against a real SQL Server instance without a dedicated SQL Server test machine. The CI pipeline started a SQL Server container, deployed the SSDT schema, ran the tSQLt tests, and discarded the container when done. Clean slate for every CI run, no shared state between builds.

This closed the last gap in the database CI pipeline: the test SQL Server instance that had previously been a shared resource with all the drift and collision problems that implies. The containerized test instance was as ephemeral and reproducible as the application code it was testing.

The Signal About Microsoft's Direction

SQL Server on Linux wasn't just a technical decision — it was a signal. Microsoft was moving away from "everything runs on Windows" to "our products run where your workloads run." SQL Server following the data to Linux and containers, rather than requiring data workloads to come to Windows, was a prerequisite for SQL Server remaining relevant as the industry moved toward Linux-first container-based infrastructure. It was the right call, and recognizing it as a strategic direction change in 2016 was worth updating your assumptions about which Microsoft products were going to remain viable on non-Windows infrastructure. As always, I'm here to help.

Read more