How to Connect Docker Compose Services To Each Other

Erik A. Ekberg
3 min readNov 21, 2018

--

One Docker Compose service can connect to another service through a URL like

http://{{service-name}}:{{listening-port}}/

where {{service-name}} is the name of the service in the docker-compose.yml file and the {{listening-port}} is the port the application is listening to inside the container.

Using the below sketch of a docker-comopse.yml file which defines three services: our-database-service, our-api-service, and our-client-service

# docker-compose.yml
version: "3"

services:
our-database-service:
image: some-db-image
environment:
PORT: 5432

our-api-service:
image: some-api-image
command: run-my-api PORT=3000 HOST=0.0.0.0

our-client-service:
image: some-client-image
command: run-my-client --port 3000 --host 0.0.0.0
ports:
- 8080:3000

our-api-service can fetch data from our-database-service through http://our-database-service:5432/.

What is a Listening Port?

A listening port is the port that an application is accepting requests on.

Using our sketch of a docker-compose.yml file from above, we see that our-database-service is listening on port 5432.

Alternatively, our-api-service and our-client-service are both listening on port 3000 as part of their command arguments.

Even though our-api-service and our-client-service are listening to the same port, they are doing so in their own isolated containers. So there is no conflict.

However, a listening port is different than an exposed port.

What is an Exposed Port?

An exposed port is how Docker compose makes our-client-service accessible at localhost:8080, denoted by 8080:3000 in our docker-compose.yml file.

Using the ports argument with something like 8080:3000 tells Docker Compose to map the listening port 3000 to localhost:8080.

Reviewing our docker-compose.yml we see that our-client-service is exposed on localhost:8080 but none of our other services are. So we can’t access them through localhost.

What are Client-Side Pitfalls?

Docker Compose can only connect to other services if the request is being made from inside the container.

For backend services like our-database-service and our-api-service this isn’t an issue. But for our-client-service connecting to other services may be a problem.

For example, if we wrote a function like

async getDataFromOurApiService = () => {
const res = await fetch('http://our-api-service:3000/my-data.json')
return res.data
}

and ran it server side, Docker Compose would correctly route the request to our-api-service.

However, if we ran this function in a browser, at localhost:8080 for example, we would get an error like Unable to resolve "our-api-services:3000".

The reason this error occurs is because the browser has no awareness of Docker Compose and how it is handling these magic URLs: so it just throws an error.

To work around this, we either need to expose our-api-service to localhost just like we did our-client-service or use some kind of proxy library that only ever runs inside the container.

If we exposed our-api-service our docker-compose.yml file might look something like this:

# docker-compose.yml
version: "3"

services:
our-database-service:
image: some-db-image
environment:
PORT: 5432

our-api-service:
image: some-api-image
command: run-my-api PORT=3000 HOST=0.0.0.0
ports:
- 5000:3000 # Expose service to locahost:5000

our-client-service:
image: some-client-image
command: run-my-client --port 3000 --host 0.0.0.0
ports:
- 8080:3000

--

--

Erik A. Ekberg
Erik A. Ekberg

Written by Erik A. Ekberg

Software engineer with a background in human psychology and data analytics who affords both customer and engineer delight through Agile software architectures.

Responses (1)