DOCKER COMPOSE — MULTI-CONTAINER ARCHITECTURE docker-compose network nginx Web Server port: 80:80 port: 443:443 app Application port: 3000 depends_on: db db PostgreSQL port: 5432 volume: pgdata redis Cache port: 6379 volume: redis docker-compose.yml defines all services, networks, and volumes in one file

Gerenciar containers Docker individuais com longos comandos docker run rapidamente se torna impraticável quando sua aplicação requer múltiplos serviços interconectados. O Docker Compose resolve esse problema permitindo que você defina e gerencie aplicações multi-container usando um único arquivo YAML declarativo. Para administradores de sistemas, o Docker Compose é uma ferramenta indispensável que preenche a lacuna entre o gerenciamento manual de containers e plataformas de orquestração completas como o Kubernetes.

Este guia cobre tudo o que você precisa saber para usar o Docker Compose de forma eficaz em cenários do mundo real, desde entender a sintaxe YAML até implantar stacks prontas para produção com health checks, limites de recursos e políticas de reinicialização adequadas.

Pré-requisitos

Antes de começar, certifique-se de que você tem:

  • Docker Engine instalado no seu sistema. Se você ainda não fez isso, siga nosso guia: Como Instalar o Docker no Ubuntu 22.04 e 24.04
  • Compreensão básica dos conceitos do Docker (imagens, containers, volumes, redes)
  • Um editor de texto e acesso ao terminal com privilégios sudo

O Que É o Docker Compose?

O Docker Compose é uma ferramenta para definir e executar aplicações Docker multi-container. Em vez de executar múltiplos comandos docker run com flags complexas, você descreve toda a stack da sua aplicação em um arquivo docker-compose.yml (ou compose.yml) e então inicia tudo com um único comando.

Um caso de uso típico pode incluir uma aplicação web que depende de um banco de dados, uma camada de cache e um reverse proxy. O Docker Compose permite que você defina todos esses serviços, suas configurações, redes e volumes em um só lugar, tornando sua infraestrutura reproduzível e controlável por versionamento.

Compose V2 vs. V1: O Que Mudou

O Docker Compose passou por uma evolução significativa:

RecursoCompose V1 (Legado)Compose V2 (Atual)
Comandodocker-compose (binário separado)docker compose (plugin CLI)
LinguagemPythonGo
PerformanceInicialização mais lentaSignificativamente mais rápido
InstalaçãoInstalação separada necessáriaIncluído com o Docker Engine
StatusDescontinuado (EOL Junho 2023)Desenvolvimento ativo

Se você instalou o Docker seguindo nosso guia, o plugin Compose V2 já está disponível como docker compose (observe o espaço em vez do hífen). Todos os exemplos neste artigo usam a sintaxe V2.

Importante: Se você encontrar scripts ou documentação que referenciam docker-compose (com hífen), substitua por docker compose (com espaço). A funcionalidade é a mesma, mas o V1 não é mais mantido.

Anatomia de um Arquivo docker-compose.yml

Um arquivo Compose tem três seções de nível superior: services, networks e volumes. Aqui está a estrutura básica:

# Opcional: especifique a versão do arquivo Compose (não necessário no V2)
services:
  # Defina os serviços da sua aplicação aqui
  web:
    image: nginx:latest
    ports:
      - "80:80"

  database:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: secretpassword

networks:
  # Defina redes personalizadas aqui (opcional)

volumes:
  # Defina volumes nomeados aqui (opcional)

Serviços

Serviços são os containers que compõem sua aplicação. Cada definição de serviço pode incluir:

  • image ou build: A imagem Docker a ser usada ou um Dockerfile para construir
  • ports: Mapeamentos de porta entre host e container
  • environment: Variáveis de ambiente
  • volumes: Pontos de montagem para persistência de dados
  • depends_on: Dependências de inicialização de serviço
  • restart: Política de reinicialização
  • networks: Quais redes conectar
  • command: Sobrescrever o comando padrão do container

Redes

Por padrão, o Docker Compose cria uma única rede para sua aplicação e todos os serviços podem se comunicar entre si usando seus nomes de serviço como hostnames. Você pode definir redes personalizadas para um controle mais granular sobre a comunicação entre serviços:

services:
  web:
    networks:
      - frontend
      - backend

  api:
    networks:
      - backend

  database:
    networks:
      - backend

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true  # Sem acesso externo

Neste exemplo, o serviço web pode se comunicar tanto com api quanto com database através da rede backend, enquanto api e database são isolados do acesso externo pela flag internal: true na rede backend. A rede frontend pode ser usada para tráfego de um reverse proxy.

Volumes

Volumes persistem dados além do ciclo de vida de um container. Volumes nomeados são a abordagem recomendada:

services:
  database:
    image: mysql:8.0
    volumes:
      - db_data:/var/lib/mysql       # Volume nomeado
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql  # Bind mount

volumes:
  db_data:
    driver: local

Variáveis de Ambiente e Arquivos .env

Codificar segredos e valores de configuração diretamente no seu docker-compose.yml é uma prática ruim. O Docker Compose suporta arquivos .env para gerenciar variáveis de ambiente de forma limpa.

Crie um arquivo .env no mesmo diretório do seu docker-compose.yml:

# .env
MYSQL_ROOT_PASSWORD=supersecretpassword
MYSQL_DATABASE=myapp
MYSQL_USER=appuser
MYSQL_PASSWORD=apppassword
NGINX_PORT=8080

Referencie essas variáveis no seu arquivo Compose:

services:
  database:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}

  web:
    image: nginx:latest
    ports:
      - "${NGINX_PORT}:80"

Você também pode passar uma diretiva env_file para carregar variáveis de ambiente diretamente em um container:

services:
  api:
    image: myapp:latest
    env_file:
      - ./app.env

Dica de Segurança: Sempre adicione arquivos .env ao seu .gitignore para evitar commitar segredos acidentalmente no controle de versão. Forneça um arquivo .env.example com valores de exemplo para fins de documentação.

Exemplo Prático 1: Stack Nginx + PHP-FPM + MySQL

Esta é uma stack clássica de aplicação web que muitos administradores de sistemas precisam implantar:

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - ./app:/var/www/html:ro
      - ./nginx/ssl:/etc/nginx/ssl:ro
    depends_on:
      php:
        condition: service_started
    networks:
      - webnet
    restart: unless-stopped

  php:
    image: php:8.3-fpm-alpine
    volumes:
      - ./app:/var/www/html
      - ./php/php.ini:/usr/local/etc/php/php.ini:ro
    environment:
      DB_HOST: mysql
      DB_NAME: ${MYSQL_DATABASE}
      DB_USER: ${MYSQL_USER}
      DB_PASSWORD: ${MYSQL_PASSWORD}
    depends_on:
      mysql:
        condition: service_healthy
    networks:
      - webnet
    restart: unless-stopped

  mysql:
    image: mysql:8.0
    volumes:
      - mysql_data:/var/lib/mysql
      - ./mysql/init:/docker-entrypoint-initdb.d:ro
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
    networks:
      - webnet
    restart: unless-stopped

networks:
  webnet:
    driver: bridge

volumes:
  mysql_data:

Pontos-chave sobre esta configuração:

  • Health checks no MySQL garantem que o PHP-FPM não inicie até que o banco de dados esteja realmente pronto para aceitar conexões, não apenas quando o container inicia.
  • Montagens somente leitura (:ro) são usadas para arquivos de configuração e certificados SSL como medida de segurança.
  • Volumes nomeados (mysql_data) persistem os dados do banco de dados independentemente do ciclo de vida do container.
  • restart: unless-stopped garante que os serviços se recuperem automaticamente de falhas, mas permaneçam parados se você pará-los manualmente.

Exemplo Prático 2: Stack de Monitoramento com Prometheus + Grafana

Uma stack de monitoramento é essencial para qualquer ambiente de produção:

services:
  prometheus:
    image: prom/prometheus:latest
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus_data:/prometheus
    ports:
      - "9090:9090"
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--storage.tsdb.retention.time=30d'
      - '--web.console.libraries=/etc/prometheus/console_libraries'
      - '--web.console.templates=/etc/prometheus/consoles'
    networks:
      - monitoring
    restart: unless-stopped

  grafana:
    image: grafana/grafana:latest
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning:ro
    ports:
      - "3000:3000"
    environment:
      GF_SECURITY_ADMIN_USER: ${GRAFANA_ADMIN_USER:-admin}
      GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD}
      GF_USERS_ALLOW_SIGN_UP: "false"
    depends_on:
      - prometheus
    networks:
      - monitoring
    restart: unless-stopped

  node-exporter:
    image: prom/node-exporter:latest
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.rootfs=/rootfs'
      - '--path.sysfs=/host/sys'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
    networks:
      - monitoring
    restart: unless-stopped

  cadvisor:
    image: gcr.io/cadvisor/cadvisor:latest
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
      - /dev/disk/:/dev/disk:ro
    privileged: true
    devices:
      - /dev/kmsg:/dev/kmsg
    networks:
      - monitoring
    restart: unless-stopped

networks:
  monitoring:
    driver: bridge

volumes:
  prometheus_data:
  grafana_data:

Esta stack fornece métricas em nível de container (cAdvisor), métricas em nível de host (Node Exporter), armazenamento e consulta de métricas (Prometheus) e dashboards de visualização (Grafana).

Comandos Comuns do Docker Compose

Aqui está uma referência abrangente dos comandos que você usará com mais frequência:

# Iniciar todos os serviços em segundo plano
docker compose up -d

# Iniciar e forçar reconstrução de imagens
docker compose up -d --build

# Parar todos os serviços
docker compose down

# Parar e remover volumes (CUIDADO: destrói dados)
docker compose down -v

# Visualizar serviços em execução
docker compose ps

# Visualizar logs de todos os serviços
docker compose logs

# Seguir logs de um serviço específico
docker compose logs -f nginx

# Reiniciar um serviço específico
docker compose restart php

# Escalar um serviço (executar múltiplas instâncias)
docker compose up -d --scale worker=3

# Executar um comando em um container de serviço em execução
docker compose exec mysql mysql -u root -p

# Baixar imagens mais recentes para todos os serviços
docker compose pull

# Visualizar a configuração Compose resolvida
docker compose config

# Listar todos os projetos Compose em execução no sistema
docker compose ls

Dica: O comando docker compose config é extremamente útil para depuração. Ele exibe o YAML completamente resolvido após a substituição de variáveis, mostrando exatamente o que o Docker Compose vai executar.

Considerações para Produção

Políticas de Reinicialização

Sempre defina uma política de reinicialização para serviços de produção:

services:
  web:
    restart: unless-stopped  # Recomendado para a maioria dos serviços

Políticas disponíveis:

  • no (padrão): Nunca reiniciar
  • always: Sempre reiniciar, mesmo se parado manualmente
  • on-failure: Reiniciar apenas se o container sair com um código diferente de zero
  • unless-stopped: Reiniciar a menos que seja explicitamente parado pelo administrador

Health Checks

Health checks permitem que o Docker saiba se um serviço está realmente funcionando, não apenas em execução:

services:
  api:
    image: myapi:latest
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

Health checks são usados pelas condições do depends_on e pelo Docker para determinar quando reiniciar containers com problemas de saúde.

Limites de Recursos

Impeça que um único serviço consuma todos os recursos disponíveis do sistema:

services:
  worker:
    image: myworker:latest
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 128M

Nota: Limites de recursos em deploy.resources funcionam com docker compose up em versões recentes do Compose V2. Em versões mais antigas, eles só se aplicavam ao modo Docker Swarm.

Configuração de Logging

Configure logging por serviço para evitar esgotamento do disco:

services:
  web:
    image: nginx:latest
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

Melhores Práticas de Segurança

  • Nunca execute containers como root a menos que seja absolutamente necessário. Use a diretiva user.
  • Defina read_only: true em containers que não precisam escrever no sistema de arquivos.
  • Remova capacidades Linux desnecessárias usando cap_drop: [ALL] e adicione de volta apenas o que for necessário com cap_add.
  • Use gerenciamento de segredos para dados sensíveis em vez de variáveis de ambiente em texto puro em produção.
services:
  api:
    image: myapi:latest
    read_only: true
    user: "1000:1000"
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    tmpfs:
      - /tmp

Solução de Problemas do Docker Compose

Serviço Não Consegue Conectar a Outro Serviço

Se um serviço não consegue alcançar outro, verifique se eles estão na mesma rede e use o nome do serviço (não o nome do container) como hostname:

# Verificar a quais redes um serviço está conectado
docker compose exec web ping database

# Inspecionar a rede
docker network ls
docker network inspect <project_name>_default

Conflitos de Porta

Se você ver bind: address already in use, outro processo está usando essa porta:

# Descobrir o que está usando a porta 80
sudo lsof -i :80
# ou
sudo ss -tlnp | grep :80

Altere o mapeamento de porta do host no seu arquivo Compose, por exemplo de "80:80" para "8080:80".

Containers Reiniciando Continuamente

Verifique os logs para entender por que um container está falhando:

docker compose logs --tail=50 <service_name>

# Verificar o código de saída do container
docker compose ps -a

Causas comuns incluem variáveis de ambiente ausentes, permissões de arquivo incorretas em volumes montados e dependências não estando prontas.

Performance Lenta de Bind Mount no macOS

Se você está desenvolvendo no macOS e experimenta acesso lento a arquivos com bind mounts, considere usar volumes Docker em vez de bind mounts para diretórios grandes, ou use a opção de montagem :cached:

volumes:
  - ./app:/var/www/html:cached

Conclusão

O Docker Compose transforma a forma como administradores de sistemas implantam e gerenciam aplicações multi-container. Ao definir sua infraestrutura em um arquivo YAML declarativo, você ganha reprodutibilidade, controle de versão e um fluxo de trabalho de implantação com um único comando. Os exemplos e melhores práticas cobertos neste guia devem lhe dar uma base sólida para implantar tudo, desde stacks web simples até soluções de monitoramento abrangentes.

Para seus próximos passos, considere explorar:

  • Perfis do Docker Compose para gerenciar diferentes ambientes (desenvolvimento, staging, produção)
  • Docker Compose watch para atualizações automáticas de serviço durante o desenvolvimento
  • Ferramentas de orquestração como Docker Swarm ou Kubernetes para implantações multi-host

Certifique-se de que o Docker está instalado corretamente no seu sistema primeiro. Se você ainda não fez isso, siga nosso guia completo de instalação: Como Instalar o Docker no Ubuntu 22.04 e 24.04.