United Kingdom: +44 (0)208 088 8978

Running dev dependencies in Docker

Matt demonstrates how Docker can be used to make dev environment setup simple.

We're hiring Software Developers

Click here to find out more


Docker provides the ability to package and run an application in a loosely isolated environment called a container.

That sentence from the official Docker overview nicely captures what Docker's all about.

Using Docker, it's very easy to install a service that you need for your development environment. This is because often someone else has created an image - a template for a container - for the service and published that image to a registry. When you want to run the service, you just need to tell Docker to create and run a container from that image. Because the container is isolated, it contains all of the dependencies that the service requires, so you don't need to worry about having them installed on your machine. From the perspective of a program running inside a container, the container seems like a computer - it has its own filesystem and programs can run inside it.

Docker desktop

If you're developing on Windows, the easiest way to get started with Docker is to install Docker Desktop.

Running SQL Server and Azurite using Docker

In Compositional IT, two services that we often need when developing our applications are SQL Server and Azurite. SQL Server is a relational database management system; Azurite emulates Azure storage services locally. Microsoft provides official SQL Server Docker images and Azurite Docker images on Docker Hub.

To run the latest SQL Server 2019 image, first make sure that Docker is running, then enter the following in your shell:

docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=yourStrong(!)Password" -p 1433:1433 -d mcr.microsoft.com/mssql/server:2019-latest
  • -e sets environment variables inside the container. In this case you're accepting the license agreement and setting a password for the database system administrator.
  • -p says which ports of the Docker container should be exposed on the host machine. Here you're saying connections to port 1433 on the computer hosting Docker should be forwarded to port 1433 of the Docker container.
  • -d means run the container in "detached" mode. That is, rather than printing the container's console logs to the shell you ran the command in, print an ID for the container and have it run in the background.
  • mcr.microsoft.com/mssql/server:2019-latest is a reference to the image that we want to run - before the colon is the image name and after is the tag, to differentiate between different versions.

To run the Azurite container, it's

docker run -p 10000:10000 -p 10001:10001 -p 10002:10002 mcr.microsoft.com/azure-storage/azurite

Port 10000 is for the blob service, 10001 is for the queue service and 10002 is for the table service.

Once you've done that, you should see the containers running in Docker desktop. If you don't specify a name (--name ...), Docker autogenerates one for you; don't be surprised if you end up with something wacky!

Running Docker containers shown in Docker desktop

Now that the services are running, you can interact with them. For example you can evaluate the following in FSI:

#r "nuget:Azure.Storage.Queues"
open Azure.Storage.Queues

let queueServiceClient = QueueServiceClient "UseDevelopmentStorage=true"
queueServiceClient.CreateQueue "blog-queue"
// Check logs for something like this:
// - - [29/Apr/2022:11:22:08 +0000] "PUT /devstoreaccount1/blog-queue HTTP/1.1" 201 -

let queueClient = queueServiceClient.GetQueueClient "blog-queue"
queueClient.SendMessage "testing"
// Check logs for something like this:
// - - [29/Apr/2022:11:22:12 +0000] "POST /devstoreaccount1/blog-queue/messages HTTP/1.1" 201 -

#r "nuget: System.Data.SqlClient"
#r "nuget: Dapper"
open System.Data.SqlClient
open Dapper

let connection = new SqlConnection "Data Source=localhost,1433;Database=tempdb;User ID=sa;Password=yourStrong(!)Password"

connection.Execute "CREATE TABLE #Test (Value INT, Text NVARCHAR(10))"
connection.Execute "INSERT INTO #Test VALUES (1, 'One'), (2, 'Two')"

type Number = { Value: int; Text: string }
connection.Query<Number> "SELECT * FROM #Test" |> Seq.map (fun n -> n.Text)
// Check FSI for `val it : seq<string> = seq ["One"; "Two"]`


  • Docker allows you to run services in containers, rather than downloading and installing the service on your machine directly.
  • Many service developers provide official images on registries like Docker Hub, and there are plenty more images provided by the community.
  • Once you have installed Docker (which is not hard), launching a container by referencing an image from a registry is easy.
  • Because the containers are isolated, you don't need to worry about the services' dependencies cluttering your machine. This is especially nice if you want to stop using a service; there's no need to uninstall it and its dependencies, just remove the container!

Put simply, Docker makes it easy to manage your dev environment. Stay tuned - next time Ryan will show how to start required Docker containers as part of launching a SAFE app! 🚀