Comunicando dois containers com docker-compose

Você já deve ter se deparado com a situação de ter que fazer dois containers diferentes conversarem entre si no seu ambiente de desenvolvimento. Eu passei por isso recentemente e decidi criar este pequeno tutorial para mostrar, da forma mais simples possível, como resolver esse problema. Bora pro que interessa!
Para este tutorial, vou partir do princípio que você já tem o Docker instalado na sua máquina e sabe o básico de seus comandos e do docker-compose. Caso ainda não tenha o Docker, você pode instalá-lo seguindo o link oficial.
Iniciando nosso arquivo docker-compose
Para começarmos, crie uma pasta onde achar melhor. No meu caso, vou criar em /home/meuusuario/www/meuprojeto.
Vamos utilizar como exemplo a imagem oficial do Node.js (node:20-alpine, uma versão moderna, leve e segura) para ambos os serviços. A nossa API vai consumir um arquivo chamado db.json, que vamos criar dentro de uma nova pasta chamada api:
{
"clients": [
{
"id": "59761c23b30d971669fb42ff",
"isActive": true,
"age": 36,
"name": "Monkey D. Luffy",
"gender": "male",
"company": "One Piece",
"email": "luffy@onepiece.com",
"phone": "+1 (890) 311-1111",
"address": "Vila Foosha, Reino Goa, East Blue, 1015"
}
]
}
O serviço do json-server
Agora, criamos o arquivo compose.yaml (ou docker-compose.yml) na raiz do projeto. Começamos configurando o serviço da nossa API fake. Em vez de usar imagens de terceiros desatualizadas, vamos rodar o json-server direto via npx em um container Node oficial:
services:
json-server:
container_name: json-server
image: node:20-alpine
command: npx -y json-server --watch /api/db.json --port 80 --host 0.0.0.0
ports:
- "80:80"
volumes:
- ./api:/api
restart: always
networks:
- my_network
networks:
my_network:
Aviso: certifique-se de que não tem nenhum serviço rodando na porta 80 da sua máquina local.
Explicando os comandos principais:
- services: onde definimos os containers que serão criados.
- image: a imagem base. Aqui usamos o
node:20-alpine. - command: o comando executado ao iniciar o container. O
npx -y json-serverbaixa e roda o pacote temporariamente, economizando espaço. - ports: mapeia a porta do container para a porta do nosso computador.
- volumes: espelha a pasta do nosso computador para dentro do container, garantindo que nossos dados persistam e atualizem em tempo real.
- networks: criamos uma rede isolada e conectamos nosso container a ela (o segredo da comunicação interna está aqui!).
Criando nossa aplicação Node.js
Agora vamos preparar o terreno para o nosso segundo container. Primeiro, crie o arquivo package.json na raiz:
{
"name": "my-project",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"dev": "nodemon index.js"
}
}
Em seguida, crie o index.js. Como o Axios é assíncrono, vamos usar async/await para garantir que o Node espere a resposta da API:
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
app.get('/api/clients', async (req, res) => {
try {
// O NOME do serviço do outro container vira a nossa URL!
const response = await axios.get('http://json-server/clients');
res.status(200).json(response.data);
} catch (error) {
res.status(500).json({ error: 'Falha na comunicação' });
}
});
app.listen(3000, () => {
console.log('NodeJS server rodando na porta 3000 🚀');
});
Para envelopar isso, crie um Dockerfile na raiz do projeto usando uma versão moderna e leve do Node:
FROM node:20-alpine
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
Juntando tudo e testando
Agora vamos adicionar o serviço do Node.js no nosso compose.yaml, logo abaixo do json-server:
nodejs:
build:
context: .
container_name: nodejs
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
networks:
- my_network
ports:
- "3000:3000"
command: npm run dev
depends_on:
- json-server
Com tudo no lugar, abra o terminal e rode utilizando o novo formato do Docker Compose V2 (sem o hífen):
docker compose up --build
Acesse http://localhost:3000/api/clients no seu navegador. O que aconteceu?
Quando seu container Node fez a requisição para http://json-server/clients, o Docker funcionou como um DNS interno. Como ambos os containers estão na rede my_network, ele entende que json-server é o outro container e faz a ponte direto pela porta 80. Tudo isso sem precisar expor IP de rede local e de forma isolada!

Livro Recomendado: Descomplicando o Docker
Recomendação essencial de leitura para você dominar de vez o ecossistema Docker e parar de sofrer com ambientes de desenvolvimento.
Gostou de algum produto citado? Considere adquiri-los através dos links disponíveis nesta página para apoiar o meu trabalho. Você não paga nada a mais por isso e me ajuda a continuar produzindo conteúdos de qualidade!