31

I have a docker-compose file that creates a starts SQL Server. This is working fine. I can connect to the database and see the master database.

What I am trying to do is create a new database, and add a table and some data to that table. I have been unable to find an example of doing this using SQL Server. All the examples I have seen are either using PostgreSQL or Mysql.

I have tried to adapt this example Docker Compose MySQL Multiple Database

I have created an init directory with a file called 01.sql and the only thing in it is

CREATE DATABASE `test`;

My docker-compose.yml looks like this

services:
    db:
        image: "mcr.microsoft.com/mssql/server"
        ports:
            - 1433:1433
        volumes:             
            - ./init:/docker-entrypoint-initdb.d
        environment:
            SA_PASSWORD: "password123!"
            ACCEPT_EULA: "Y"

When I run docker-compose up

I'm not seeing anything in the logs that implies it's even trying to load this file. When I check the database I do not see any new database either.

I am at a loss to understand why this isn't working for SQL Server but the tutorial implies that it works for MySql. Is there a different command for SQL Server?

4
  • The way MySQL does it, by running scripts in the /docker-entrypoint-initdb.d directory, is MySQL specific. The MSSQL docs for the Docker image has a suggestion on how to initialize a database in the "How to use this image" section. At first glance it seems to require more work than the MySQL approach. hub.docker.com/_/microsoft-mssql-server Commented Nov 12, 2021 at 11:30
  • Microsoft's own SQL Server-in-a-container documentation points to the mssql-node-docker-demo-app demo repo. You can pretty much ignore most of their container setup - the interesting part is to use CMD or ENTRYPOINT to run your own entrypoint.sh script that starts the SQL Server service and then sits in a retry loop invoking sqlcmd to connect and import data from a setup.sql script. Commented Nov 12, 2021 at 11:40
  • @AlwaysLearning yes I noticed the ENTRYPOINT and CMD in a couple of other scripts i ran across. This will take more research thanks for the tip Commented Nov 12, 2021 at 11:48
  • Seeing this is (obviously) very popular, I think this should be built-in and thus made an issue for it. Please upvote there, if you agree. Commented Mar 28 at 10:12

5 Answers 5

44

Here is a docker-compose.yml sample with SQL server Microsoft image (without build) that does not rely on delay (I am not sure if it is a reliable way to wait for the SQL Server instance to start).

This approach is using additional setup container that executes your initialization SQL scripts as soon as main SQL Server container initializes itself.

I used the same mcr.microsoft.com/mssql/server:2019-latest image as it contains SQL Server tools in it, but feel free to use any image you like with SQL Server tools installed in it.

version: '3.8'


volumes:
  sqlserver_data:
  
services:
  sqlserver:
    image: mcr.microsoft.com/mssql/server:2019-latest
    environment:
      - ACCEPT_EULA=Y
      - SA_PASSWORD=${Sa_Password:-#password123sdJwnwlk}
      - MSSQL_PID=Developer
    ports:
      - 1433:1433
    volumes:
      - sqlserver_data:/var/opt/mssql
    restart: always
    healthcheck:
      test: ["CMD-SHELL", "/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P ${Sa_Password:-password123} -Q 'SELECT 1' || exit 1"]
      interval: 10s
      retries: 10
      start_period: 10s
      timeout: 3s
  sqlserver.configurator:
    image: mcr.microsoft.com/mssql/server:2019-latest
    volumes:
      - ./init:/docker-entrypoint-initdb.d
    depends_on:
      sqlserver:
        condition: service_healthy
    command: >
      bash -c '
      /opt/mssql-tools/bin/sqlcmd -S sqlserver -U sa -P ${Sa_Password:-#password123sdJwnwlk} -d master -i docker-entrypoint-initdb.d/init.sql;
      echo "All done!";
      '

This compose file require you to add init.sql file to init subfolder. Here is an example to create new user:

USE [master];
GO

IF NOT EXISTS (SELECT * FROM sys.sql_logins WHERE name = 'newuser')
BEGIN
    CREATE LOGIN [newuser] WITH PASSWORD = '#password123sdJwnwlk', CHECK_POLICY = OFF;
    ALTER SERVER ROLE [sysadmin] ADD MEMBER [newuser];
END
GO

`

Update 07.11.2024: To address comments from Wojtek vanDer and Igor Kanshyn The app did not start with the exact above script because the provided default password: password123 did not match the default SQL Server password policy.

I have updated the default Passwords in scripts so now it should work fine.

Sign up to request clarification or add additional context in comments.

4 Comments

Great work! Worked like a charm! I even tested this docker-compose setup with mcr.microsoft.com/mssql/server:2022-latest image and it also worked!
Unfortunately this is not a working a solution - requires manual tweaking and only shows a concept how might it be done: dependency failed to start: container sql-server-sqlserver-1 is unhealthy
Same problem here the heat check is never successful
On sqlserver2022 the path to sqlcmd is different and you likely also need -C to trust the server certificate: test: ["CMD-SHELL", "/opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -P MyPassword1234! -Q 'SELECT 1' || exit 1"]
36

After quite a bit of Googling and combining four or five very old tutorials, I got this working. Ensuring that you are using Linux line endings is critical with these scripts.

Docker-compose.yml

version: '3'

services:
  db:
    build: ./Db
    ports:
        - 1433:1433

Db/DockerFile

# Choose ubuntu version
FROM mcr.microsoft.com/mssql/server:2019-CU13-ubuntu-20.04

# Create app directory
WORKDIR /usr/src/app

# Copy initialization scripts
COPY . /usr/src/app
             
# Set environment variables, not have to write them with the docker run command
# Note: make sure that your password matches what is in the run-initialization script 
ENV SA_PASSWORD password123!
ENV ACCEPT_EULA Y
ENV MSSQL_PID Express

# Expose port 1433 in case accessing from other container
# Expose port externally from docker-compose.yml
EXPOSE 1433

# Run Microsoft SQL Server and initialization script (at the same time)
CMD /bin/bash ./entrypoint.sh

Db/entrypoint.sh

# Run Microsoft SQl Server and initialization script (at the same time)
/usr/src/app/run-initialization.sh & /opt/mssql/bin/sqlservr

Db/run-initialization.sh

# Wait to be sure that SQL Server came up
sleep 90s

# Run the setup script to create the DB and the schema in the DB
# Note: make sure that your password matches what is in the Dockerfile
/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P password123! -d master -i create-database.sql

Db/create-database.sql

CREATE DATABASE [product-db]
GO

USE [product-db];
GO

CREATE TABLE product (
    Id INT NOT NULL IDENTITY,
    Name TEXT NOT NULL,
    Description TEXT NOT NULL,
    PRIMARY KEY (Id)
);
GO

INSERT INTO [product] (Name, Description)
VALUES 
('T-Shirt Blue', 'Its blue'),
('T-Shirt Black', 'Its black'); 
GO

Tip: If you change any of the scripts after running it the first time you need to do a docker-compose up --build to ensure that the container is built again or it will just be using your old scripts.

Connect:

host:  127.0.0.1
Username: SA
Password:  password123!

7 Comments

Does it work for you without inserting a context in the Docker-compose.yml file?
Excellent answer. I update image, make minor changes and place here. mssql_server
Why do you run Microsoft SQL Server and initialization script (at the same time) if you then wait to be sure that SQL Server came up?
Usually you'd want the DB data to persist on an external drive. How would that work here? During the build process you'd be creating and filling a database and that data would be stored inside the image, but then once you deploy it and data is added to the DB, would it be stored in the container and not the external volume?
IIR this was a demo project for a client that just needed a microservice with static data that would spin up when started. it was not intended to be production level deployed application.
|
3

So things have changed a bit since these answers came out, here's my solution that uses the latest 2022 sql Server image. I also have a few other minor QOL features to this answer

First our docker-compose

  my_db_container:
    image: mcr.microsoft.com/mssql/server:2022-latest
    environment:
      ACCEPT_EULA: "Y"
      MSSQL_SA_PASSWORD: "Hey!Don't look here. Bad!"
    ports:
      - "1433:1433"
    networks:
      - dev_network
    volumes:
      - ./db-init/init.sql:/db/init.sql
      - ./db-init/entrypoint.sh:/entrypoint.sh
    entrypoint: ["/bin/bash", "/entrypoint.sh"]

First our docker-compose sets up the db image. The networks: - dev_network is to make it easier to reach my db container from my app container. The volumes' are how my container has access to the scripts it needs to create the db. In my docker-compose project I also need to have the two files specified in volumes (under db-init)

init.sql

in my case I just need the database created. All my migrations happen in the app code, but everything breaks if the db doesn't exist, so this is all I needed.

IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = N'Jeff')
BEGIN
    CREATE DATABASE [Jeff];
END

entrypoint.sh.

This is the one that I had to fight with, and the one that doesn't work anymore with Linda or Koryakov's solutions (AI was similarly no help because it probably used their answers... I went down a solid rabbit hole). Microsoft change the location of their tools folder, so mssql-tools18 needs to be referenced. If it changes again - just inspect the file system on your docker image and see what they renamed or moved it to this time. I've also used a solution from elsewhere to reduce the lead time for the db creation as much as possible, rather than waiting X seconds, it waits in a loop till it can do a handshake.

#!/bin/bash

# Start SQL Server in the background
/opt/mssql/bin/sqlservr &

# Wait until SQL Server is ready
echo "Waiting for SQL Server to be available..."
until /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "$MSSQL_SA_PASSWORD" -C -Q "SELECT 1" > /dev/null 2>&1; do
  sleep 1
  echo "Still Waiting for SQL Server to be available..."
done

# Run your init script
echo "Running init.sql..."
/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "$MSSQL_SA_PASSWORD" -C -i /db/init.sql
echo "Finished running init.sql."
# Keep container alive
wait

Comments

1

Following to the answer, I faced few issues related to access and environment variables, corrected those issues and shared the working version of the below docker-compose.yml

volumes:
  sqlserver_data1:
  
services:
  sqlserver:
    image: mcr.microsoft.com/mssql/server:2022-latest
    environment:
      - ACCEPT_EULA=Y
      - MSSQL_SA_PASSWORD=1StrongPwd!!
      - MSSQL_PID=Developer
    user: root
    ports:
      - 1433:1433
    volumes:
      - sqlserver_data1:/var/opt/mssql
    restart: always
    healthcheck:
      test: ["CMD-SHELL", "/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P 1StrongPwd!! -C -Q 'SELECT 1' || exit 1"]
      interval: 10s
      retries: 10
      start_period: 10s
      timeout: 3s
  sqlserver.configurator:
    image: mcr.microsoft.com/mssql/server:2022-latest
    user: root
    volumes:
      - ./init:/docker-entrypoint-initdb.d
    depends_on:
      sqlserver:
        condition: service_healthy
    command: >
      bash -c '
      /opt/mssql-tools18/bin/sqlcmd -S sqlserver -U sa -P 1StrongPwd!! -C -d master -i docker-entrypoint-initdb.d/init.sql;
      echo "All done!";
      '

Notes:

  1. If you are running on the machine co-exists with SQL-Server (SSMS) stop those services.
  2. Add "-C" as parameter "sqlcmd" to avoid certificate related issue

Comments

-1

Docker images for mysql and postgres know about docker-entrypoint-initdb.d directory, and how to process it.

For example, for mysql, here is Dockerfile

It runs script docker-entrypoint.sh:

COPY docker-entrypoint.sh /usr/local/bin/
RUN ln -s usr/local/bin/docker-entrypoint.sh /entrypoint.sh # backwards compat
ENTRYPOINT ["docker-entrypoint.sh"]

And docker-entrypoint.sh runs sql scripts from docker-entrypoint-initdb.d directory:

docker_process_init_files /docker-entrypoint-initdb.d/*

Looks like SQL Server docker image does not have processing for docker-entrypoint-initdb.d, you need to study SQL Server Dockerfile or documentation, probably there is some tools to init DB. If there not - you can create your own Docker image, basing on original:

Dockerfile:

FROM mcr.microsoft.com/mssql/server
# implement init db from docker-entrypoint-initdb.d

2 Comments

Wait a minute. So the docker image for MySql contains scripts that can be used by docker compose? I thought they just packaged the database. I didnt know you could add stuff. (mind blown)
docker-compose used to create container from image (image described in Dockerfile) and run container; and to map directory containing sql scripts into docker-entrypoint-initdb.d directory inside container. Dockerfile contains scripts to install and run sql server and to run sql scripts from docker-entrypoint-initdb.d (in case of mysql and postgres); MS SQL Server Dockerfile has different scripts for initialization :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.