104

I would like to create a MySQL database using environment variables in docker-compose.yml file, but it is not working. I have the following code:

# The Database
database:
  image: mysql:5.7
  volumes:
    - dbdata:/var/lib/mysql
  restart: always
  environment:
    MYSQL_ROOT_PASSWORD: secret
    MYSQL_DATABASE: homestead
    MYSQL_USER: root
    MYSQL_PASSWORD: secret
  ports:
    - "33061:3306"

Could someone explain the function of this vars?

rlandster
  • 7,294
  • 14
  • 58
  • 96
Pedrog
  • 1,262
  • 3
  • 10
  • 12

11 Answers11

141

There is also an option to provide an init file for mysql container which will be applied each time a container is created.

database:
    image: mysql:5.7
    ports:
        - "33061:3306"
    command: --init-file /data/application/init.sql
    volumes:
        - ./init.sql:/data/application/init.sql
    environment:
        MYSQL_ROOT_USER: root
        MYSQL_ROOT_PASSWORD: secret
        MYSQL_DATABASE: homestead
        MYSQL_USER: root
        MYSQL_PASSWORD: secret

Such file (init.sql) could contain your initial database structure and data - for example:

CREATE DATABASE IF NOT EXISTS dev;
CREATE DATABASE IF NOT EXISTS test;
USE dev;
CREATE TABLE IF NOT EXISTS (...);
vlio20
  • 8,955
  • 18
  • 95
  • 180
marszczybrew
  • 1,654
  • 1
  • 10
  • 8
  • /data/application/init.sql this file should be located in the host machine? or inside mysql container? – Madhan Ayyasamy Jan 31 '19 at 15:52
  • @MadhanAyyasamy see the updated answer. Since `/data/application/init.sql` is an argument to mysql which runs in the container, the path has to be "internal" too. The file can get there by being bind mounted. – marszczybrew Jan 31 '19 at 18:17
  • 3
    can i run multiple sql file? – Hamzawey Mar 26 '19 at 20:25
  • 2
    This will result in a **directory** called `/data/application/init.sql` on the container, _unless_ the file already exists on container. About mounting a host file to appear as a file in the container read about **bind mounts** at the [docker docs](https://docs.docker.com/storage/bind-mounts/). – kaicarno May 29 '19 at 22:18
  • Where to put my init.sql file locally? – Matley Jun 29 '19 at 18:28
  • 1
    @Matley the volume paths inside `docker-compose.yml` are relative to that file, so if it's in root project directory and our mount contains `./init.sql:/data/application/init.sql` then the `init.sql` file has to also be in the root directory – marszczybrew Jul 04 '19 at 06:25
  • beautiful way to create two databases upon starting a docker-compose – vintagexav Feb 07 '23 at 15:19
53

The database is probably already initialized and the configuration is stored in /var/lib/mysql. Since you defined a volume for that location the config will survive a restart. The MySQL image will not reconfigure the database over and over again, it only does this once.

volumes:
    - dbdata:/var/lib/mysql

If your database is empty you can reset the database by performing docker-compose down -v where the -v removes the volumes defined in the volume section. See https://docs.docker.com/compose/reference/down/. On the next docker-compose up the MySQL image will start fresh and will initialize the database with the configuration you've provided throug the environment section.

Marcin Orlowski
  • 72,056
  • 11
  • 123
  • 141
  • Oh yeah! thank you very much, it must was that because now it is getting values from env. vars. – Pedrog Apr 10 '17 at 16:59
  • Thank you very much sir! I couldn't import my database sql gz file automatically since volume was already created without it the first time. The -v argument fixed this. – coder_1432 May 11 '18 at 18:14
  • Oh my days this was so useful. I spent the best part of a whole work day trying to figure out why docker up wasn't creating the DB. (It had been deleted). This fixed it. Surprisingly doing `docker stop $(docker ps -a -q)`, `docker rm $(docker ps -a -q)` and `docker rmi $(docker images -q)` did NOT solve this. I would've thought nuking everything would have worked. – simonw16 Jan 17 '19 at 23:03
29

The official MySQL docker image added support for init scripts in their base image. They document the feature under their "Initializing a fresh instance" on the Docker Hub page.

Here are the steps I took to solve creating multiple database and users in the MySQL docker image:

  1. Create the init file (the Docker image recognizes .sh, .sql, and .sql.gz files) named setup.sql in the local directory named .docker
  2. Place the commands inside setup.sql (see below for an example)
  3. Mount the setup script into the directory f within the docker-compose.yaml file (see below for an example)
  4. Run docker-compose up -d and MySQL will run the code inside setup.sql

Note: the script will run files alphabetically, so keep that in mind.

Example docker-compose.yaml

version: "3.5"
services:
    mysql:
        image: mysql
        ports:
            - 3306:3306
        environment:
            MYSQL_ROOT_PASSWORD: SomeRootPassword1!
            MYSQL_USER: someuser
            MYSQL_PASSWORD: Password1!
            MYSQL_DATABASE: somedatabase
        volumes:
            - .docker/setup.sql:/docker-entrypoint-initdb.d/setup.sql
            - db_data:/var/lib/mysql
volumes:
    db_data:

Example setup.sql

-- create the databases
CREATE DATABASE IF NOT EXISTS projectone;

-- create the users for each database
CREATE USER 'projectoneuser'@'%' IDENTIFIED BY 'somepassword';
GRANT CREATE, ALTER, INDEX, LOCK TABLES, REFERENCES, UPDATE, DELETE, DROP, SELECT, INSERT ON `projectone`.* TO 'projectoneuser'@'%';

FLUSH PRIVILEGES;
Jason M.
  • 563
  • 1
  • 5
  • 10
  • Doesnt work, I get the error: myflashcards_db | 2020-04-08T18:51:57.851680Z 1 [ERROR] Failed to open the bootstrap file /data/application/init.sql myflashcards_db | 2020-04-08T18:51:57.851718Z 1 [ERROR] 1105 Bootstrap file error, return code (0). Nearest query: 'SET @@sql_log_bin = @sql_log_bin; – Matley Apr 08 '20 at 18:52
24

Answering your question ...

One thing I use to do when building a new docker container is understand what the image I pull from does when is builded.

In your docker-compose.yml tou have this

# The Database
database:
  image: mysql:5.7

This is the image you pull from, "mysql:5.7"

Dockerhub is a repository where you can find info of this images.

Do a google search "mysql:5.7 dockerhub"

First result is https://hub.docker.com/_/mysql/

There you have your image 5.7, if you click on 5.7 you have this

https://github.com/docker-library/mysql/blob/607b2a65aa76adf495730b9f7e6f28f146a9f95f/5.7/Dockerfile

Which is the Dockerfile from the image, you can have a look at interesting things that happen when building the image.

One of this is ENTRYPOINT ["docker-entrypoint.sh"]

This is the file that got executed when image is ready

I you go one level up in the repo you will see this file

https://github.com/docker-library/mysql/tree/607b2a65aa76adf495730b9f7e6f28f146a9f95f/5.7

The you can see your environment variables being used to create new database etc...

file_env 'MYSQL_DATABASE'
        if [ "$MYSQL_DATABASE" ]; then
            echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" | "${mysql[@]}"
            mysql+=( "$MYSQL_DATABASE" )
fi
jonhid
  • 2,075
  • 1
  • 11
  • 18
17

For version 2 of docker-compose you'll .yml or .yaml can look like this:

version: '2'
volumes:
 dbdata:

services:
 mysql:
  image: mysql:5.7
  container_name: mysql
  volumes:
    - dbdata:/var/lib/mysql
  restart: always
  environment:
    - MYSQL_ROOT_PASSWORD=secret
    - MYSQL_DATABASE=homestead
    - MYSQL_USER=root
    - MYSQL_PASSWORD=secret
  ports:
    - "33061:3306"

start it with docker-compose up -d and check:

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                     NAMES
a3567fb78d0d        mysql:5.7           "docker-entrypoint..."   2 minutes ago       Up 2 minutes        0.0.0.0:33061->3306/tcp   mysql

docker exec -it a3567fb78d0d bash
root@a3567fb78d0d:/# mysql -u root -p homestead
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 7
Server version: 5.7.17 MySQL Community Server (GPL)

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| homestead          |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.00 sec)

Your volume will be persisted in docker volume nameoffolder_dbdata (/var/lib/docker/volumes/...)

lvthillo
  • 28,263
  • 13
  • 94
  • 127
  • That is no exactly that i asking but it is interesting. I wonder if the envinronment vars are to create the database as well. Because in the docker-compose.yml figure the database name, why? – Pedrog Apr 10 '17 at 12:29
  • 1
    I don't fully understand your reply/question but yes, the env var's are creating a database with the name homestead. Check my answer when I perform show databases. You see the homestead db, also I'm authenticating on that db (-p) – lvthillo Apr 10 '17 at 13:46
  • I already have the container. I want to create the database for the container. Can you please tell me that how to create the docker-compose file for the container? – fiza khan Dec 22 '17 at 07:03
  • Use an image which contains a database in the container like mysql or postgres? if you want to install your own db you have to write a Dockerfile and build the image in docker-compose – lvthillo Dec 22 '17 at 07:28
11

If I understand your question correctly, you want to to have a container with a specific database in it. Like have a MySQL container with CREATE DATABASE mydb, etc. already executed. If so you need to use docker-entrypoint-initdb.d: https://docs.docker.com/samples/library/mysql/#docker-secrets

When the official MySQL container is started for the first time, a new database will be created first. Then it will execute files with extensions .sh, .sql and .sql.gz that are found in /docker-entrypoint-initdb.d. So all you need to do is create /docker-entrypoint-initdb.d directory and put your initialisation script there.

Gene S
  • 495
  • 5
  • 14
8

The "Initializing a fresh instance" here https://hub.docker.com/_/mysql/ states to copy all .sql,.sh,.sql.gz files into /docker-entrypoint-initdb.d folder. Below is the docker-compose file that I am using for plenty of my projects and all working fine with latest version of MySQL

version: '3.6'
services:   
  mysql:
      environment:
          - MYSQL_DATABASE=root
          - MYSQL_ROOT_PASSWORD=changeme
          - MYSQL_USER=dbuser
          - MYSQL_PASSWORD=changeme
      command: 
         - --table_definition_cache=100
         - --performance_schema=0
         - --default-authentication-plugin=mysql_native_password
         - --innodb_use_native_aio=0
      volumes: 
          - ./init:/docker-entrypoint-initdb.d
      container_name: mysqldb
      image: mysql  

and my init folder has an init.sql file containing the entire SQL data dump that I need to recreate when my container starts.

Below commands are used to limit memory consumed by MySQL container to 100 MB:

         - --table_definition_cache=100
         - --performance_schema=0

NOTE: This doesn't persist your MySQL data if you wish to persist your data then use the below configuration

version: '3.6'
services:   
  mysql:
      environment:
          - MYSQL_DATABASE=root
          - MYSQL_ROOT_PASSWORD=changeme
          - MYSQL_USER=dbuser
          - MYSQL_PASSWORD=changeme
      command: 
         - --table_definition_cache=100
         - --performance_schema=0
         - --default-authentication-plugin=mysql_native_password
         - --innodb_use_native_aio=0
      volumes: 
          - ./init:/docker-entrypoint-initdb.d
          - ./dbdata:/var/lib/mysql
      container_name: mysqldb
      image: mysql  

where dbdata is a folder you need to create on your host system.

Marcin Orlowski
  • 72,056
  • 11
  • 123
  • 141
Rohit Salecha
  • 893
  • 11
  • 9
7

This setup implies your database already exists. Otherwise you have to create it with an init script or manually.

Init scripts(any .sh, .sql and .sql.gz files) are expected in /docker-entrypoint-initdb.d/ folder within your docker container.

All you have to do is to simply add this init script as a volume to your docker-compose file.

database:
 image: mysql:latest
  volumes:
    - ./init-script.sql:/docker-entrypoint-initdb.d/init-script.sql

  ...

Your init-script.sql file might look like this:

CREATE DATABASE IF NOT EXISTS some_name;
USE some_name;
Mikhail Chuprynski
  • 2,404
  • 2
  • 29
  • 42
2

From the documentation of the Mysql docker image

MYSQL_DATABASE This variable is optional and allows you to specify the name of a database to be created on image startup. If a user/password was supplied (see below) then that user will be granted superuser access (corresponding to GRANT ALL) to this database.

So, just add it to your docker-compose.yml file:

    db:
        image: mysql:8.0.29
        environment:
            MYSQL_DATABASE: '[your-database-name]'
juanmorschrott
  • 573
  • 5
  • 25
2

Keep in mind if you want to docker-compose automatically create database from MYSQL_DATABASE your data folder (dbdata) must be empty!

    # The Database
    database:
      image: mysql:5.7
      volumes:
        - dbdata:/var/lib/mysql   <--- check local dbdata is empty!
      restart: always
      environment:
        MYSQL_ROOT_PASSWORD: secret
        MYSQL_DATABASE: homestead 

    etc...

No need initial scripts, just:

docker-compose down -v

Clean dbdata

docker-compose up

./dbdata/homestead - exists

Alex K
  • 2,613
  • 1
  • 20
  • 31
1

if you want to create database your docker-compose.yml will looks like if you want to use Dockerfile

version: '3.1'

services:
  php:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 80:80
    volumes:
      - ./src:/var/www/html/
  db:
    image: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: example
    volumes:
      - mysql-data:/var/lib/mysql

  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080
volumes:
  mysql-data:

your docker-compose.yml will looks like if you want to use your image instead of Dockerfile

version: '3.1'   

services:
  php:
    image: php:7.4-apache
    ports:
      - 80:80
    volumes:
      - ./src:/var/www/html/
  db:
    image: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: example
    volumes:
      - mysql-data:/var/lib/mysql

  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080
volumes:

if you want to store or preserve data of mysql then must remember to add two lines in your docker-compose.yml

volumes:
  - mysql-data:/var/lib/mysql

and

volumes:
  mysql-data:

after that use this command

docker-compose up -d

now your data will persistent and will not be deleted even after using this command

docker-compose down

extra:- but if you want to delete all data then you will use

docker-compose down -v
Hassan Saeed
  • 6,326
  • 1
  • 39
  • 37