Running SQL Server on Apple Silicon - Updated

Last week I purchased a shiny new MacBook Air with an M2 processor. After I got all the standard stuff up and running, I set out to learn how to run SQL Server containers on this new hardware. This post shows you how to run SQL Server on Apple Silicon using colima.

Colima is a container runtime that runs a Linux VM on your Mac. This Linux VM runs using the Virtualization framework hypervisor native in MacOS. Your containers will run inside this virtual machine.

Install Required Software

Docker Desktop for Mac

First, you’ll need Docker Desktop. Well, technically, you don’t need Docker Desktop. Docker Desktop provides both the command line tooling and the container runtime. I’m installing Docker because it will provide the command line interface that I’m used to when working with containers, the docker command. We’re not going to use Docker as the container runtime. As I write this post, Docker Desktop for Mac recently added MacOS Virtualization framework support, but SQL Server doesn’t run in this environment. I plan to watch this space to see if Docker provides similar functionality in upcoming releases.

Update 22 Jan 23: As predicted, Docker Desktop now supports the Use of Rosetta for x86/amd64 emulation on Apple Silion. Open up your Docker Desktop Settings, click Features in Development and check Use of Rosetta for x86/amd64 emulation on Apple Silion. Once enabled and docker restarts, you can jump right to the section Starting a SQL Server Container. You may be thinking, why would I still go the Colima route? Well, Docker has changed their licensing model and the Colima solution may be a good alternative for you.

Rosetta

Rosetta 2

Next, you’ll need Rosetta 2, which provides emulation for x86 CPU architectures. In other words, software built for x86 can run on Apple Silicon.

softwareupdate --install-rosetta

Installing Colima

You can install Colima with brew MacOS package manager. If you need help installing brew, check out this link.

You can then brew to install Colima.

brew install colima

Configuring Colima

Setting the Colima virtual machine template configuration

Now, you need to configure Colima’s virtual machine template. Like Docker for Mac, Colima uses a virtual machine to provide a Linux kernel for containers running on MacOS. This virtual machine needs some configuration to run x86 containers.

To configure the VM, you use the command colima template. This will open Colima’s yaml configuration file in your default text editor.

First, let’s configure the VM to use the MacOS’s Virtualization framework (vz) rather than the default, which is qemu.

Around line 121, change vmType: qemu to vmType: vz.

Next, around line 133, change mountType: sshfs to mountType: virtiofs

Next, not required, but reccomended you need to update the CPU and memory allocated to the VM.

Around line 9, change cpu: 2 to cpu 4 and around line 18 change memory: 2 to memory: 4.

Starting up Colima

Now, you’ll create a virtual machine from the template file configured in the previous section. You execute the command colima start. This creates and boots up a VM. And it changes the context of your docker command to point to the Colima container runtime rather than Docker Desktop’s. So when you use docker commands, they are executed against this VM rather than the Docker Desktop installation. As a note, the settings you set in the template file above can be passed as runtime parameters for the colima start command. But my preference is to have them persistent in a file.

bash-3.2$ colima start
INFO[0000] starting colima                              
INFO[0000] runtime: docker                              
INFO[0000] creating and starting ...                     context=vm
INFO[0014] provisioning ...                              context=docker
INFO[0014] starting ...                                  context=docker
INFO[0019] done       

Working with Contexts

A second ago, I used the term context. A context is a configuration for your docker CLI tools telling the tools which container runtime endpoint to execute the commands against. When you install Docker Desktop, your default context is the Docker container runtime endpoint. When you start up Colima, it adds a new context and configures your default docker context to point to this new endpoint. You can easily switch between these to launch containers in either container runtimes using the command docker context use [context name]. To get a listing of which contexts are available on your system, you can use docker context list and you will get this output.

docker context list 
NAME                TYPE                DESCRIPTION                               DOCKER ENDPOINT                                 KUBERNETES ENDPOINT   ORCHESTRATOR
colima *            moby                colima                                    unix:///Users/aen/.colima/default/docker.sock                         
default             moby                Current DOCKER_HOST based configuration   unix:///var/run/docker.sock                                           swarm
desktop-linux       moby                                                          unix:///Users/aen/.docker/run/docker.sock

Notice the * next to colima, this means it is the default. So docker commands will execute and start containers in this container runtime. Note you can also define which context you want your commands to run against for each execution with the parameter --context rather than relying on the default context configuration.

At this point, you no longer need the docker container runtime. To conserve your systems resources, you can quit docker and set it to not restart when your computer starts up.

Starting a SQL Server Container

OK, so now that everything is up and running, let’s start a SQL Server container. Most of these parameters are familiar to you if you’ve been working with SQL Server in containers. There is one new one, --platform linux/amd64. This gives docker a hint on which container image to pull.

docker run \
    --env 'ACCEPT_EULA=Y' \
    --env 'MSSQL_SA_PASSWORD=S0methingS@Str0ng!' \
    --name 'sqldemo1' \
    --hostname 'sqldemo1' \
    --publish 31433:1433 \
    --volume sqldemo1:/var/opt/mssql \
    --detach \
    --platform linux/amd64 mcr.microsoft.com/mssql/server:2022-RTM-ubuntu-20.04 

If you don’t specify --platform linux/amd64, everything works fine, but you’ll get this warning at startup.

WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested

Checking out what SQL Server thinks about all of this

What I like most about this is all the code I’ve written using the docker command just work, even though this container isn’t running in the docker container runtime but rather Colima.

With that up and running, we can use docker logs sqldemo1 to get some information about this container.

SQL Server thinks it’s running on an x64 CPU.

2023-01-02 15:59:32.97 Server      Microsoft SQL Server 2022 (RTM) - 16.0.1000.6 (X64) 
        Oct  8 2022 05:58:25 
        Copyright (C) 2022 Microsoft Corporation
        Developer Edition (64-bit) on Linux (Ubuntu 20.04.5 LTS) <X64>

Based on our Colima virtual machine configuration, you can see SQL Server had access to four CPUs. The MacBook Air I have has eight cores. But this CPU topology is coming from the Colima VM.

2023-01-02 15:59:32.98 Server      SQL Server detected 1 sockets with 4 cores per socket and 4 logical processors per socket, 4 total logical processors; using 4 logical processors based on SQL Server licensing. This is an informational message; no user action is required.

Next, we configured Colima to use 4GB of RAM. The VM has 4GB of RAM, but due to the architecture of SQL Server on Linux with SQLPAL, the default is the SQL Server process sees about 80% of that. So here we see 3142MB of memory.

2023-01-02 15:59:32.98 Server      Detected 3142 MB of RAM. This is an informational message; no user action is required.

SQL Server throwing some shade at my configuration:

2023-01-02 15:59:33.67 Server      In-Memory OLTP initialized on lowend machine.

How’s Performance?

This whole experiment isn’t a performance play. I need the ability to run SQL Server containers on my laptop in a functional way. This configuration gives me just that. And it’s pretty decent on performance too. While, I still need to do a complete evaluation of performance, here’s a restore of a backup…getting roughly 785MB/sec. That’s good enough for what I need.

USE [master]
RESTORE DATABASE [tpcc100] 
FROM  DISK = N'/var/opt/mssql/data/tpcc100.bak' 
WITH REPLACE

Database 'tpcc100' running the upgrade step from version 912 to version 913.
...
Database 'tpcc100' running the upgrade step from version 956 to version 957.
RESTORE DATABASE successfully processed 1032597 pages in 10.278 seconds (784.896 MB/sec).
Total execution time: 00:00:12.866