Communication between multiple docker-compose projects

DockerDocker Compose

Docker Problem Overview


I have two separate docker-compose.yml files in two different folders:

  • ~/front/docker-compose.yml
  • ~/api/docker-compose.yml

How can I make sure that a container in front can send requests to a container in api?

I know that --default-gateway option can be set using docker run for an individual container, so that a specific IP address can be assigned to this container, but it seems that this option is not available when using docker-compose.

Currently I end up doing a docker inspect my_api_container_id and look at the gateway in the output. It works but the problem is that this IP is randomly attributed, so I can't rely on it.

Another form of this question might thus be:

  • Can I attribute a fixed IP address to a particular container using docker-compose?

But in the end what I'm looking after is:

  • How can two different docker-compose projects communicate with each other?

Docker Solutions


Solution 1 - Docker

You just need to make sure that the containers you want to talk to each other are on the same network. Networks are a first-class docker construct, and not specific to compose.

# front/docker-compose.yml
version: '2'
services:
  front:
    ...
    networks:
      - some-net
networks:
  some-net:
    driver: bridge

...

# api/docker-compose.yml
version: '2'
services:
  api:
    ...
    networks:
      - front_some-net
networks:
  front_some-net:
    external: true

> Note: Your app’s network is given a name based on the “project name”, which is based on the name of the directory it lives in, in this case a prefix front_ was added

They can then talk to each other using the service name. From front you can do ping api and vice versa.

Solution 2 - Docker

UPDATE: As of compose file version 3.5:

This now works:

version: "3.5"
services:
  proxy:
    image: hello-world
    ports:
      - "80:80"
    networks:
      - proxynet

networks:
  proxynet:
    name: custom_network

docker-compose up -d will join a network called 'custom_network'. If it doesn't exist, it will be created!

root@ubuntu-s-1vcpu-1gb-tor1-01:~# docker-compose up -d
Creating network "custom_network" with the default driver
Creating root_proxy_1 ... done

Now, you can do this:

version: "2"
services:
  web:
    image: hello-world
    networks:
      - my-proxy-net
networks:
  my-proxy-net:
    external:
      name: custom_network

This will create a container that will be on the external network.

I can't find any reference in the docs yet but it works!

Solution 3 - Docker

Just a small adittion to @johnharris85's great answer, when you are running a docker compose file, a "default" network is created so you can just add it to the other compose file as an external network:

# front/docker-compose.yml 
version: '2' 
  services:   
    front_service:
    ...

...

# api/docker-compose.yml
version: '2'
services:
  api_service:
    ...
    networks:
      - front_default
networks:
  front_default:
    external: true

For me this approach was more suited because I did not own the first docker-compose file and wanted to communicate with it.

Solution 4 - Docker

All containers from api can join the front default network with following config:

# api/docker-compose.yml

...

networks:
  default:
    external:
      name: front_default

See docker compose guide: using a pre existing network (see at the bottom)

Solution 5 - Docker

The previous posts information is correct, but it does not have details on how to link containers, which should be connected as "external_links".

Hope this example make more clear to you:

  • Suppose you have app1/docker-compose.yml, with two services (svc11 and svc12), and app2/docker-compose.yml with two more services (svc21 and svc22) and suppose you need to connect in a crossed fashion:

  • svc11 needs to connect to svc22's container

  • svc21 needs to connect to svc11's container.

So the configuration should be like this:

this is app1/docker-compose.yml:


version: '2'
services:
svc11:
container_name: container11
[..]
networks:
- default # this network
- app2_default # external network
external_links:
- container22:container22
[..]
svc12:
container_name: container12
[..]




networks:
default: # this network (app1)
driver: bridge
app2_default: # external network (app2)
external: true

networks: default: # this network (app1) driver: bridge app2_default: # external network (app2) external: true

this is app2/docker-compose.yml:


version: '2'
services:
svc21:
container_name: container21
[..]
networks:
- default # this network (app2)
- app1_default # external network (app1)
external_links:
- container11:container11
[..]
svc22:
container_name: container22
[..]




networks:
default: # this network (app2)
driver: bridge
app1_default: # external network (app1)
external: true

networks: default: # this network (app2) driver: bridge app1_default: # external network (app1) external: true

Solution 6 - Docker

Since Compose 1.18 (spec 3.5), you can just override the default network using your own custom name for all Compose YAML files you need. It is as simple as appending the following to them:

networks:
  default:
    name: my-app

> The above assumes you have version set to 3.5 (or above if they don't deprecate it in 4+).

Other answers have pointed the same; this is a simplified summary.

Solution 7 - Docker

UPDATE: As of docker-compose file version 3.5:

I came across a similar problem and I solved it by adding a small change in one of my docker-compose.yml project.

For instance, we have two API's scoring and ner. Scoring API needs to send a request to the ner API for processing the input request. In order to do that they both are supposed to share the same network.

Note: Every container has its own network which is automatically created at the time of running the app inside docker. For example ner API network will be created like ner_default and scoring API network will be named as scoring default. This solution will work for version: '3'.

As in the above scenario, my scoring API wants to communicate with ner API then I will add the following lines. This means Whenever I create the container for ner API then it automatically added to the scoring_default network.

networks:
  default:
      external:
        name: scoring_default

ner/docker-compose.yml

version: '3'
services:
  ner:
    container_name: "ner_api"
    build: .
    ...

networks:
  default:
      external:
        name: scoring_default

scoring/docker-compose.yml

version: '3'
services:
  api:
    build: .
    ...

We can see this how the above containers are now a part of the same network called scoring_default using the command:

> docker inspect scoring_default

{
    "Name": "scoring_default",
        ....
    "Containers": {
    "14a6...28bf": {
        "Name": "ner_api",
        "EndpointID": "83b7...d6291",
        "MacAddress": "0....",
        "IPv4Address": "0.0....",
        "IPv6Address": ""
    },
    "7b32...90d1": {
        "Name": "scoring_api",
        "EndpointID": "311...280d",
        "MacAddress": "0.....3",
        "IPv4Address": "1...0",
        "IPv6Address": ""
    },
    ...
}

Solution 8 - Docker

Everybody has explained really well, so I'll add the necessary code with just one simple explanation.

Use a network created outside of docker-compose (an "external" network) with docker-compose version 3.5+.

Further explanation can be found here.

First docker-compose.yml file should define network with name giveItANamePlease as follows.

networks:
  my-network:
    name: giveItANamePlease
    driver: bridge

The services of first docker-compose.yml file can use network as follows:

networks:
  - my-network

In second docker-compose file, we need to proxy the network by using the network name which we have used in first docker-compose file, which in this case is giveItANamePlease:

networks:
  my-proxy-net:
    external:
      name: giveItANamePlease

And now you can use my-proxy-net in services of a second docker-compose.yml file as follows.

networks:
  - my-proxy-net

Solution 9 - Docker

You can add a .env file in all your projects containing COMPOSE_PROJECT_NAME=somename.

COMPOSE_PROJECT_NAME overrides the prefix used to name resources, as such all your projects will use somename_default as their network, making it possible for services to communicate with each other as they were in the same project.

NB: You'll get warnings for "orphaned" containers created from other projects.

Solution 10 - Docker

> For using another docker-compose network you just do these(to share networks between docker-compose): > > 1. Run the first docker-compose project by up -d > 2. Find the network name of the first docker-compose by: docker network ls(It contains the name of the root directory project) > 3. Then use that name by this structure at below in the second docker-compose file.

second docker-compose.yml

version: '3'
services:
  service-on-second-compose:  # Define any names that you want.
    .
    .
    .
    networks:
      - <put it here(the network name that comes from "docker network ls")>

networks:
  - <put it here(the network name that comes from "docker network ls")>:
    external: true

Solution 11 - Docker

I would ensure all containers are docker-compose'd to the same network by composing them together at the same time, using:

docker compose --file ~/front/docker-compose.yml --file ~/api/docker-compose.yml up -d

Solution 12 - Docker

If you are

  • trying to communicate between two containers from different docker-compose projects and don't want to use the same network (because let's say they would have PostgreSQL or Redis container on the same port and you would prefer to not changing these ports and not use it at the same network)
  • developing locally and want to imitate communication between two docker compose projects
  • running two docker-compose projects on localhost
  • developing especially Django apps or Django Rest Framework (drf) API and running app inside container on some exposed port
  • getting Connection refused while trying to communicate between two containers

And you want to

  • container api_a communicate to api_b (or vice versa) without the same "docker network"

(example below)

you can use "host" of the second container as IP of your computer and port that is mapped from inside Docker container. You can obtain IP of your computer with this script (from: https://stackoverflow.com/questions/166506/finding-local-ip-addresses-using-pythons-stdlib):

import socket
def get_ip():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        # doesn't even have to be reachable
        s.connect(('10.255.255.255', 1))
        IP = s.getsockname()[0]
    except:
        IP = '127.0.0.1'
    finally:
        s.close()
    return IP

Example:

project_api_a/docker-compose.yml:

networks:
  app-tier:
    driver: bridge

services:
  api:
    container_name: api_a
    image: api_a:latest
    depends_on:
      - postgresql
    networks:
      - app-tier

inside api_a container you are running Django app: manage.py runserver 0.0.0.0:8000

and second docker-compose.yml from other project:

project_api_b/docker-compose-yml :

networks:
  app-tier:
    driver: bridge

services:
  api:
    container_name: api_b
    image: api_b:latest
    depends_on:
      - postgresql
    networks:
      - app-tier

inside api_b container you are running Django app: manage.py runserver 0.0.0.0:8001

And trying to connect from container api_a to api_b then URL of api_b container will be: http://<get_ip_from_script_above>:8001/

It can be especially valuable if you are using even more than two(three or more) docker-compose projects and it's hard to provide common network for all of it - it's good workaround and solution

Solution 13 - Docker

So many answers!

First of all, avoid hyphens in entities names such as services and networks. They cause issues with name resolution.

Example: my-api won't work. myapi or api will work.

What worked for me is:

# api/docker-compose.yml
version: '3'

services:
  api:
    container_name: api
    ...
    ports:
      - 8081:8080
    networks:
      - mynetwork

networks:
  mynetwork:
    name: mynetwork

and

# front/docker-compose.yml
version: '3'

services:
  front:
    container_name: front
    ...
    ports:
      - 81:80
    networks:
      - mynetwork

networks:
  mynetwork:
    name: mynetwork

NOTE: I added ports to show how services can access each other, and how they are accessible from the host.

IMPORTANT: If you don't specify a network name, docker-compose will craft one for you. It uses the name of the folder the docker_compose.yml file is in. In this case: api_mynetwork and front_mynetwork. That will prevent communication between containers since they will by on different network, with very similar names.

Note that the network is defined exactly the same in both file, so you can start either service first and it will work. No need to specify which one is external, docker-compose will take care of managing that for you.

From the host

You can access either container using the published ports defined in docker-compose.yml.

You can access the Front container: curl http://localhost:81

You can access the API container: curl http://localhost:8081

From the API container

You can access the Front container using the original port, not the one you published in docker-compose.yml.

Example: curl http://front:80

From the Front container

You can access the API container using the original port, not the one you published in docker-compose.yml.

Example: curl http://api:8080

Solution 14 - Docker

version: '2'
services:
  bot:
    build: .
    volumes:
      - '.:/home/node'
      - /home/node/node_modules
    networks:
      - my-rede
    mem_limit: 100m
    memswap_limit: 100m
    cpu_quota: 25000
    container_name: 236948199393329152_585042339404185600_bot
    command: node index.js
    environment:
      NODE_ENV: production
networks:
  my-rede:
    external:
      name: name_rede_externa

Solution 15 - Docker

Another option is just running up the first module with the 'docker-compose' check the ip related with the module, and connect the second module with the previous net like external, and pointing the internal ip

example app1 - new-network created in the service lines, mark as external: true at the bottom app2 - indicate the "new-network" created by app1 when goes up, mark as external: true at the bottom, and set in the config to connect, the ip that app1 have in this net.

With this, you should be able to talk with each other

*this way is just for local-test focus, in order to don't do an over complex configuration ** I know is very 'patch way' but works for me and I think is so simple some other can take advantage of this

Solution 16 - Docker

Answer for Docker Compose '3' and up

By default Docker Compose uses a bridge network to provision inter-container communication. Read this article for more info about inter-container networking.

What matters for you, is that by default Docker Compose creates a hostname that equals the service name in the docker-compose.yml file. Consider the following docker-compose.yml:

version: '3.9'
services:
  server:
    image: node:16.9.0
    container_name: server
    tty: true
    stdin_open: true
    depends_on:
       - mongo
    command: bash

  mongo:
    image: mongo
    environment:
      MONGO_INITDB_DATABASE: my-database

When you run docker-compose up, Docker will create a default network and assigns the service name as hostname for both mongo and server.

You can now access the backend container via:

docker exec -it server bash

And now you can ping the mongo container using Dockers internal network (default on port 27017 in this case):

curl -v http://mongo:27017/my-database

That's it. The same applies for your setup.

Solution 17 - Docker

I have had a similar example where I was working with separate docker-compose files working on a docker swarm with an overlay network to do that all I had to do is change the networks parameters as so:

first docker-compose.yaml

version: '3.9'
.
.
.

networks:
  net:
    driver: overlay
    attachable: true
docker-compose -p app up

since I have specified the app name as app using -p the initial network will be app_net. Now in order to run another docker-compose with multiple services that will use the same network you will need to set these as so:

second docker-compose.yaml

version: '3.9'
.
.
.
networks:
  net-ref:
    external: true
    name: app_net
docker stack deploy -c docker-compose.yml mystack

No matter what name you give to the stack the network will not be affected and will always refer to the existing external network called app_net.

>>PS: It's important to make sure to check your docker-compose version.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJivanView Question on Stackoverflow
Solution 1 - Dockerjohnharris85View Answer on Stackoverflow
Solution 2 - DockercstruttonView Answer on Stackoverflow
Solution 3 - DockerTal JoffeView Answer on Stackoverflow
Solution 4 - DockerdedekView Answer on Stackoverflow
Solution 5 - DockerDaniel BlancoView Answer on Stackoverflow
Solution 6 - DockeremyllerView Answer on Stackoverflow
Solution 7 - DockerNomiluksView Answer on Stackoverflow
Solution 8 - DockerMuhammad Waqas DilawarView Answer on Stackoverflow
Solution 9 - DockerExagone313View Answer on Stackoverflow
Solution 10 - DockerAli HallajiView Answer on Stackoverflow
Solution 11 - DockerNauraushaunView Answer on Stackoverflow
Solution 12 - DockerRafałView Answer on Stackoverflow
Solution 13 - DockerGaelView Answer on Stackoverflow
Solution 14 - DockerPedroView Answer on Stackoverflow
Solution 15 - Dockerleonardo reyView Answer on Stackoverflow
Solution 16 - DockerRobert-Jan KuyperView Answer on Stackoverflow
Solution 17 - DockerAffes SalemView Answer on Stackoverflow