Using a database defined in external compose file

I have several applications running on the same host many of which are using a similar database.

They all have something similar to this:

  podcast_www:
    build:
        context: .
        dockerfile: compose/app/Dockerfile
    volumes:
        - ./storage/app/public:/app/public/storage
    env_file: .env
    ports:
      - "9000:9000"
  podcast_db:
    image: mysql:5.7.29
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    env_file: .env
    volumes:
        - ./data:/var/lib/mysql

I was wondering rather than bring up multiple instances of mysql. Is it possible to reference to a container running defined in an external docker-compose?

I feel like i’m doing a bit too much with docker-compose and some of this would be better suited for K8 but till i’m ready to move to K8, it would be nice to consolidate on a single DB instance.

yep, it’s possible.

Though, there is a drawback: usualy the databases only execute database initialization scripts when the datbase file is empty. As such, you will need to add the required database and users for each application you are going to add yourself.

If you create a network from the cli. You can declare it in the compose.yml files as “external”, then assign your containers to this network. You need to think, rather you want to use a single “database” network, which will result in all applications will be able to communicate using the database network, OR wether you create a distinct network per application - which will make the database to be available in n-networks…

Thanks @meyay.

For future reference for anyone who is looking for something similar this is what I ended up with.

Defining a shared DB connection. I threw a PSQL one in there as well if anyone uses that over mysql

version: '3.7'
services:
  shared_mysql:
    container_name: shared_mysql
    image: mysql:5.7.29
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    env_file: .env
    networks:
       - shared_backend
    volumes:
        - ./mysql_data:/var/lib/mysql
        - ./mysql_init:/docker-entrypoint-initdb.d
    healthcheck:
      test: ["CMD-SHELL", 'mysql --database=$$MYSQL_DATABASE --password=$$MYSQL_ROOT_PASSWORD --execute="SELECT 1 + 1" --skip-column-names -B']
      interval: 10s
      timeout: 5s
      retries: 5

  shared_psql:
    image: postgres:12
    container_name: shared_psql
    volumes:
      - ./psql_data:/var/lib/postgresql/data
      - ./psql_init:/docker-entrypoint-initdb.d
    restart: always
    env_file: .env
    networks:
       - shared_backend

networks:
    shared_backend:
        name: shared_backend
        # use a custom driver, with no options
        driver: bridge

My actual app then looks like this:

version: '3.7'
services:
  podcast_www:
    build:
        context: .
        dockerfile: compose/app/Dockerfile
    volumes:
        - ./storage/app/public:/app/public/storage
    env_file: .env
    networks:
       - shared_backend
    ports:
      - "9000:9000"
    external_links:
      - shared_mysql

networks:
    shared_backend:
        name: shared_backend
        external: true


Like it was mentioned there is no bootstrapping of the database once data exists. So you’ll have to create a new user manually. I keep the init scripts in the respective directories even if they’re not being executed automatically.

MySQL

CREATE DATABASE podcast;
CREATE USER 'podcast_user'@'%' IDENTIFIED BY 'Secret';
GRANT ALL PRIVILEGES ON podcast.* TO 'podcast_user'@'%';

PSQL:

CREATE DATABASE podcast;
CREATE USER podcast_user WITH encrypted password 'Secret';
GRANT ALL privileges ON DATABASE podcast TO podcast_user;

also,

shared_docker.service:

[Unit]
Description = Shared DB
After=docker.service
Requires=docker.service

[Service]
Type=simple
WorkingDirectory=/home/docker_user/shared
ExecStart=/usr/bin/docker-compose up
ExecStop=/usr/bin/docker-compose  stop
ExecReload =/usr/bin/docker-compose restart
User=docker_user
Group=docker
Restart=always
RestartSec=3


[Install]
WantedBy=multi-user.target

I think this should as well. podcast.service

[Unit]
Description = Podcast Website
After=shared_docker.service
Requires=mysql_docker.service

[Service]
Type=simple
WorkingDirectory=/home/docker_user/podcast
ExecStart=/usr/bin/docker-compose -f production.yml up 
ExecStop=/usr/bin/docker-compose -f production.yml stop
ExecReload =/usr/bin/docker-compose -f production.yml restart
User=docker_user
Group=docker
Restart=always
RestartSec=3


[Install]
WantedBy=multi-user.target

2 Likes