How to connect docker compose services by name (or alias) | by Erik A. Ekberg | Medium
How to connect docker compose services by name (or alias)
Docker Compose is a fantastic tool for Docker container orchestration locally, but it does not provide good examples for how to connect your containers together once they are running. Something I will explain in this article.
Below is an examples of a docker-compose.yml
file which defines (1) some kind of database service, (2) some kind of backend API services, and (3) some kind of front-end client service.
# docker-compose.yml
version: "3"services:
our-database-service:
image: some-db-image
our-api-service:
image: some-api-image
command: run-my-api PORT=3000 HOST=0.0.0.0our-client-service:
image: some-client-image
command: run-my-client --port 3000 --host 0.0.0.0
ports:
- 8080:3000
Starting with our-api-service
, we notice that PORT=3000
is part of its command
argument. This PORT=3000
is the running port for our API code, which is the internal port that our API code is listening to internally to its container. In a similar manner with our-client-service
, we also added --port 3000
to its command
argument so that it too has a running port of 3000
inside its own, separate container. This means that both our API and client have a running port of 3000
. Lastly, I want to point out the additional ports
argument for our-client-service
.
By default Docker Compose spins up containers on our local computer but does not let us access them with our browser through localhost
. For example, even though both our-client-service
and our-api-service
both have a running port of 3000
, if we visit http://localhost:3000
we will see a blank page. This is because we need to publish our running ports to our local computer through the ports
argument. In the case of our-client-service
, we are publishing our running port 3000
to http://localhost:8080
on our local computer. Now, with the our distinction between a running port versus a published port, we can connect our services together.
Pivoting back to our running port of 3000
for our-client-service
and our-api-services
. We can connect our services together by using the below format in our code:
http://[service-name]:[running-port]/
As an example, if our-client-service
wanted to fetch some sort of data from an endpoint provided by our-api-service
, we would fetch that data like such:
data = fetch( ' http://our-api-service:3000/my-data.json ' )
Similarly, if we had given our-api-service
a network alias in Docker Compose, we would do something like
http://[service-name]:[running-port]/
inside our code.
More holistic example
Since we are now familiar with running ports, published ports, and the URL structure to connect our services together, I now want to give a more holistic example.
Below is the same docker-compose.yml
file we initially started with, but with a few more details filled out.
# docker-compose.yml
version: "3"services:
our-database-service:
image: postgres:14-alpineour-api-service:
image: some-api-image
depends_on:
- our-database-service
environment:
DATABASE_URL: http://database:5432/
command: run-my-api PORT=3000 HOST=0.0.0.0
our-client-service:
image: some-client-image
depends_on:
- our-api-service
environment:
API_URL: http://our-api-service:3000/
command: run-my-client --port 3000 --host 0.0.0.0
ports:
- 8080:3000
Browser-side pitfalls
Unfortunately, Docker Compose can only connect services together if the code being running inside of its spun up container. For backend services this should never be an issue, but for client-side rendered JavaScript applications this can be an issue. JavaScript running is a user’s browser means it is not running in side its container. For example, if we wrote a function like
async getDataFromApi() {
const response = await fetch('http://our-api-service:3000/my-data.json')
return response.data
}
If we ran this function in a Node backend Docker Compose would hook into our-api-service
and return our expected data. However, if this function was run in our own browser we would get an
Unable to resolve "our-api-services:3000"
error. To work around this, we can either (1) publish our-api-service
to our local computer just like our-client-service
or (2) use some kind of proxy services or library that only runs inside its container. An example of publishing our-api-service
to localhost
would look something like this:
# docker-compose.yml
version: "3"services:
our-database-service:
image: postgres:14-alpineour-api-service:
image: some-api-image
depends_on:
- our-database-service
environment:
DATABASE_URL: http://database:5432/
command: run-my-api PORT=3000 HOST=0.0.0.0
ports:
- 8081:3000our-client-service:
image: some-client-image
depends_on:
- our-api-service
environment:
API_URL: http://localhost:8081/
command: run-my-client --port 3000 --host 0.0.0.0
ports:
- 8080:3000