O Que e o Docker Compose
No modulo anterior voce subiu um container de cada vez, na mao, com docker run. Funciona para um container. Mas e quando seu site precisa de tres ao mesmo tempo: o app, o banco de dados e um cache? Digitar tres comandos gigantes toda vez, lembrar de cada porta, cada volume, cada conexao? Esquece. O Docker Compose resolve isso: voce descreve tudo num arquivo e sobe a stack inteira com um comando so.
🧠 Analogia: O Maestro da Orquestra
Imagine uma orquestra. Cada musico (container) sabe tocar seu instrumento sozinho: o app toca, o banco toca, o cache toca. Mas se cada um comecar na hora que quiser, o resultado e barulho. O maestro (o Compose) le a partitura (o arquivo docker-compose.yml) e rege todos juntos: quem comeca primeiro, quem fala com quem, em que tom.
- •A partitura = o arquivo
docker-compose.yml - •Os musicos = cada servico (app, banco, cache)
- •O "comecar a tocar" =
docker compose up
💡 Docker Compose ja vem junto
Se voce instalou o Docker moderno (Docker Desktop ou o Docker Engine recente no Linux), o Compose ja esta instalado como um subcomando: voce chama docker compose (com espaco). Em maquinas antigas o comando era docker-compose (com hifen). Os dois fazem a mesma coisa; neste modulo usamos a forma nova com espaco.
# Conferir se o Compose esta disponivel
$ docker compose version
Docker Compose version v2.27.0
O Arquivo docker-compose.yml Basico
Toda a magia mora num unico arquivo de texto chamado docker-compose.yml. Ele e escrito em YAML, um formato simples baseado em indentacao (espacos no comeco da linha). A regra de ouro: a indentacao dita o que esta dentro do que. Vamos do mais simples ao completo.
O menor compose possivel: um servico
# docker-compose.yml
services:
app:
image: nginx:alpine
ports:
- "8080:80"
Isso descreve um servico chamado app, que usa a imagem nginx:alpine e expoe a porta 80 do container na porta 8080 da sua maquina.
services
A lista de todos os containers da stack. Cada chave aqui dentro e um servico.
image
A imagem pronta a usar (do Docker Hub). Alternativa: build: para construir do seu Dockerfile.
ports
Mapeia portas no formato "host:container". A da esquerda e a do seu PC.
💡 Sobre o "version:" no topo
Em tutoriais antigos voce vai ver uma linha version: "3.8" no comeco do arquivo. No Compose moderno (v2) essa linha e opcional e ignorada: pode comecar direto em services:. Se um arquivo de exemplo tiver o version:, nao se assuste, ainda funciona.
⚠️ Erro Comum
Problema: "yaml: line 4: mapping values are not allowed in this context" ou a stack nao sobe.
Solucao: Quase sempre e indentacao errada. YAML nao aceita Tab, so espacos. Mantenha sempre 2 espacos por nivel e nunca misture Tab com espaco. No VS Code, ative "mostrar espacos em branco" para enxergar o problema.
✓ O que FAZER
- ✓Usar sempre 2 espacos por nivel de indentacao
- ✓Colocar versoes fixas nas imagens (
nginx:1.27) - ✓Dar nomes claros aos servicos (
app,banco)
✗ O que NAO fazer
- ✗Usar Tab para indentar (quebra o YAML)
- ✗Usar so
:latestem producao (vira surpresa) - ✗Esquecer as aspas em
"8080:80"
Servicos, Redes e Volumes
Tres conceitos resolvem 90% dos casos: servicos (os containers), redes (como eles conversam) e volumes (onde os dados ficam guardados). A grande sacada do Compose: ele cria uma rede automatica para a sua stack, e dentro dela um servico chama o outro pelo nome.
👁 O que voce vai ver: comunicacao pelo nome
Voce nao usa IP. Se o servico do banco se chama banco, o app se conecta nele pela URL usando o proprio nome do servico como endereco:
# Dentro do container do app, a conexao com o banco e assim:
DATABASE_URL=postgres://user:senha@banco:5432/meubanco
# "banco" e o nome do servico, NAO um IP. O Compose resolve.
Rede: por padrao, ja vem pronta
Ao subir a stack, o Compose cria uma rede so para ela. Todos os servicos entram nessa rede automaticamente e se enxergam pelo nome. Voce so declara redes manualmente quando quer isolar grupos (ex: separar frontend de backend).
services:
app:
image: meu-app
depends_on:
- banco # garante que o banco suba antes
Volume: para os dados nao sumirem
Container e descartavel: se voce remove o container do banco, os dados vao junto. O volume guarda os dados fora do container, num lugar que sobrevive a recriacao. Declare na raiz e use dentro do servico:
services:
banco:
image: postgres:16
volumes:
- dados-db:/var/lib/postgresql/data
# Declaracao do volume na raiz do arquivo:
volumes:
dados-db:
Formato: nome-do-volume:/caminho/dentro/do/container. Mesmo derrubando e recriando o container, os dados continuam la.
⚠️ Erro Comum
Problema: "Toda vez que eu rodo docker compose down meu banco zera."
Solucao: Faltou o volume. Sem volume, os dados moram dentro do container e somem na remocao. Declare um volume nomeado para o banco. Cuidado tambem com down -v: o -v apaga os volumes de proposito.
Subir uma Stack Completa: App + Banco
Chegou a hora de juntar tudo. Vamos montar uma stack real: um app web que conversa com um banco PostgreSQL, com volume para os dados nao sumirem. Um arquivo, um comando, dois containers conversando.
O docker-compose.yml completo
# docker-compose.yml
services:
app:
build: . # constroi do Dockerfile local
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://user:senha@banco:5432/app
depends_on:
- banco
banco:
image: postgres:16
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=senha
- POSTGRES_DB=app
volumes:
- dados-db:/var/lib/postgresql/data
volumes:
dados-db:
Subir a stack inteira
O -d ("detached") solta os containers rodando em segundo plano
$ docker compose up -d
[+] Running 3/3
✓ Network meuapp_default Created
✓ Container meuapp-banco-1 Started
✓ Container meuapp-app-1 Started
Conferir que subiu
Lista os containers da stack e o estado de cada um
$ docker compose ps
NAME SERVICE STATUS PORTS
meuapp-app-1 app Up 12 seconds 0.0.0.0:3000->3000/tcp
meuapp-banco-1 banco Up 13 seconds 5432/tcp
Derrubar quando terminar
Para e remove os containers e a rede (mas mantem o volume de dados)
$ docker compose down
[+] Running 3/3
✓ Container meuapp-app-1 Removed
✓ Container meuapp-banco-1 Removed
✓ Network meuapp_default Removed
💡 Dica: up sem o -d para ver tudo de uma vez
Na primeira vez, rode docker compose up sem o -d. Os logs de todos os servicos aparecem misturados na tela, ao vivo. Otimo para ver se algo deu erro na subida. Quando estiver tudo certo, derrube com Ctrl+C e suba de novo com -d.
Logs e Debug: Quando Algo Quebra
Subiu a stack e o site nao abre? Calma, isso e normal. O Compose te da tres ferramentas para investigar: ver os logs (o que cada servico esta dizendo), ver o estado dos containers, e entrar dentro de um container para olhar de perto.
logs - Ler o que os servicos dizem
# Logs de todos os servicos, acompanhando ao vivo (-f = follow)
$ docker compose logs -f
banco-1 | database system is ready to accept connections
app-1 | Server listening on port 3000
# Logs de UM servico so
$ docker compose logs app
# Apenas as ultimas 50 linhas
$ docker compose logs --tail 50 banco
O log e o primeiro lugar para olhar. 9 em cada 10 problemas aparecem aqui em texto claro.
ps - Ver o estado de cada container
$ docker compose ps
NAME SERVICE STATUS PORTS
meuapp-app-1 app Exited (1) 4 seconds ago
meuapp-banco-1 banco Up 20 seconds 5432/tcp
Aqui o app esta como Exited (1): ele caiu. O proximo passo e olhar o log dele com docker compose logs app.
🔎 exec - Entrar dentro de um container
As vezes voce precisa olhar por dentro: rodar um comando, abrir o cliente do banco, conferir um arquivo. O exec roda um comando dentro de um container que ja esta de pe:
# Abrir um terminal dentro do container do app
$ docker compose exec app sh
/app # ls
node_modules package.json server.js
# Abrir o cliente do PostgreSQL dentro do banco
$ docker compose exec banco psql -U user -d app
app=# \dt
⚠️ Erro Comum
Problema: "O app sobe mas nao conecta no banco: connection refused."
Solucao: O depends_on garante a ordem de subida, mas nao espera o banco ficar pronto para aceitar conexoes (ele leva alguns segundos). Solucao simples: faca o app tentar reconectar algumas vezes. Solucao robusta: use healthcheck no banco + condition: service_healthy no depends_on.
Atualizacao e Rollback
Sua aplicacao esta no ar e voce quer publicar uma nova versao. Ou pior: voce publicou e deu ruim, precisa voltar rapido. Com o Compose, atualizar e voltar versao sao operacoes de poucos comandos. O segredo e fixar a tag da imagem para saber sempre qual versao esta rodando.
Atualizar para uma nova versao
Suponha que sua imagem no arquivo era meu-app:1.4 e a nova versao e a 1.5. Voce troca a tag no arquivo e atualiza so o que mudou:
# 1. Editar o compose: image: meu-app:1.4 -> image: meu-app:1.5
# 2. Baixar a nova imagem do registry
$ docker compose pull
✓ app Pulled
# 3. Recriar so os containers que mudaram
$ docker compose up -d
[+] Running 2/2
✓ Container meuapp-banco-1 Running
✓ Container meuapp-app-1 Recreated
Repare: o banco fica Running (nao mexeu) e so o app e Recreated. O Compose so toca no que mudou.
Rollback: voltar para a versao anterior
Deu ruim na 1.5? Volte a tag para 1.4 e suba de novo. Como voce fixou a versao, sabe exatamente para onde voltar.
# Editar o compose de volta: image: meu-app:1.5 -> image: meu-app:1.4
$ docker compose up -d
✓ Container meuapp-app-1 Recreated
# Pronto: em segundos voce esta na versao 1.4 de novo
✓ O que FAZER
- ✓Fixar tags de versao (
app:1.4) para poder voltar - ✓Rodar
pullantes doup -d - ✓Guardar o compose no Git (cada versao vira historico)
✗ O que NAO fazer
- ✗Usar
:latest(nao da para saber qual versao voltar) - ✗Rodar
down -vachando que e so reiniciar - ✗Atualizar direto em producao sem testar antes
🏆 Voce ja consegue rodar uma stack de verdade
Com app + banco + volume num arquivo, subindo com um comando, lendo logs, entrando nos containers e voltando versao quando precisa, voce ja tem o essencial de orquestracao local. No proximo modulo voce vai aprender a fazer essa stack subir sozinha quando o servidor liga, com o systemd.
📚 Resumo do Modulo
Proximo Modulo:
4.3 - Systemd: deixar sua stack subir sozinha quando o servidor liga e reiniciar se cair