How to share single database container to multiple docker container on same host?

I currently have 3 spring-boot applications that all access the same mysql database. But all apps are standalone.

I started creating 3 individual docker containers that can connect to the real mysql database that is still running on the host.

But of course now I also want to dockerize the mysql database.

Question: how could I share the database container to the other 3 apps?

My setup is as follows:

docker-compose.yml for mysql:

services:
  mysql:
    image: mysql:8
    ports:
      - 3306:3306
    volumes:
      - ~/apps/mysql:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=secret
      - MYSQL_PASSWORD=secret
      - MYSQL_USER=secret
      - MYSQL_DATABASE=shared-database

docker-compose.yml for application 1:

services:
    spring-boot-app-1:
        build:
            dockerfile: Dockerfile
        image: spring-boot-app1:latest
        ports:
          - 8091:8080
        extra_hosts:
          - "host.docker.internal:host-gateway"

docker-compose.yml for application 2:

services:
    spring-boot-app-2:
        build:
            dockerfile: Dockerfile
        image: spring-boot-app2:latest
        ports:
          - 8092:8080
        extra_hosts:
          - "host.docker.internal:host-gateway"

Of course I could now access the database on jdbc:mysql://host.docker.internal:3306/shared-database, but this would mean everything is routed through the default bridge network.

Can I somehow establish some kind of “direct connection” from the app container to the mysql container? Maybe be adding both to a common network? If yes, how could I tell docker the network, and let docker automatically create such a network on startup?

Actualy it’s not that hard. You just need to create a network where all containers that need to interact with each other are member of.

I would strongly suggest to create the network from the cli to detach it’s lifecylce from the docker-compose deployments.

 docker network create \
  --driver=bridge \
  --attachable \
   database

Then modify your datbase container:

services:
  mysql:
    image: mysql:8
    ports:
      - 3306:3306
    volumes:
      - ~/apps/mysql:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=secret
      - MYSQL_PASSWORD=secret
      - MYSQL_USER=secret
      - MYSQL_DATABASE=shared-database
    networks:
      database: {}

networks:
  database:
    external: true

And modify your application services the same way:

services:
    spring-boot-app-1:
        build:
            dockerfile: Dockerfile
        image: spring-boot-app1:latest
        ports:
          - 8091:8080
    networks:
      database: {}

networks:
  database:
    external: true

This will allow spring-boot-app-1 to acess the database server of your mysql service using the service name (which is mysql in your case) and the container port (which is 3306 in your case). If the service names are not unique, you will need to use network aliases.

1 Like

How would I achieve the same without having to manually create the network from CLI? I mean, let docker-compose auto generate the network?

Like I said: in those situation it IS recommended to create the network outside the compose file to de-couple it from the compose lifecycle. Appearently it is not what you want, so here we go.

Make sure to remove the existing database network, as docker networks are immutable objects - config changes to them are not applied unless deleted, then change the compose file for the database to this:

services:
  mysql:
    image: mysql:8
    ports:
      - 3306:3306
    volumes:
      - ~/apps/mysql:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=secret
      - MYSQL_PASSWORD=secret
      - MYSQL_USER=secret
      - MYSQL_DATABASE=shared-database
    networks:
      default: {}

networks:
  default:
    name: database
    attachable: true

This will make the default network (which is created for every compose file implicitly, unless you specific it explicitly) use the name database (check docker network ls) on the docker engine level (without the projectname prefix that is normaly added to the network name). The applications still need to use the external database network like in my first response.

Note: I am not 100% sure if external: true attachable: true is required or just optional for your scenario. It is a flag that allows standalone containers to be attached to the network.

2 Likes

external:true was not required, thanks for the brilliant solution!

Thanks this was helpful, I’ve been wanting to share a DB among a few containers.

Why does the network config use database: {} the curly braces notation here?

I use {} to explicitly express that I don’t to provide further configuration for this configuration item.
The result is the same regardless whether the braces are used or not.

2 Likes

Thanks, I have 2 laravel containers with a service for php-fpm, and nginx.
But now that they share an external network for the DB, the app service fails because they both try to access php-fpm on app:9000. So it looks like it’s more complex in this situation, for these 2 laravel sites to share the DB.

I want my docker-compose file to have one network for internal communication of these compose services, and one external only for DB

Can you create a new topic for your question?
Please provide all relevant details (like all involved compose files) so we actually get a chance to completely understand the situation.

I had some trouble with a database container loading a volume that was created via docker compose. So the volume had labels com.docker.compose.project: folder com.docker.compose.volume: data com.docker.compose.version: 2.3.3 , and even though my database container was separate from other docker compose lifecycles, it was somehow messed up because of the volume having labels tied to a specific docker-compose project folder.

So I strongly agree with this recommendation to create the network ourside of compose lifecycle. And also create any external volumes outside of the compose lifecycle, because those labels can mess with things on the volume. Leading to strange behavior.

I guess those volume labels were really adhered to by docker, and could not be ignored.

So I recreated a new blank volume with no labels. Then I restored my DB volume to my new blank volume. With no labels.

This solved my issue and I full decoupled my database volume and network from the app itself.

1 Like

@meyay you meant to write attachable: true here, right?

Cause @slrdko responded with external: true

You are right, I fixed it in the original post. Thank you!