Conteúdo

Sintaxe completa do compose.yaml: tutorial de Docker Compose

O nível superior de compose.yaml tem quatro seções principais:

name: myapp          # Nome do projeto (opcional, por padrão é o nome da pasta)

services:            # Contêineres a executar
  ...

volumes:             # Volumes nomeados
  ...

networks:            # Rede personalizada (opcional; uma é criada automaticamente por padrão)
  ...

A seguir, explicamos as configurações mais usadas em services de acordo com sua finalidade. No final, executamos um exemplo completo.

Serviços, rede do projeto, volume nomeado e portas mapeadas para o host em um projeto Compose
Um projeto Compose: o nome do serviço também é o nome do host; somente os serviços configurados com ports podem ser acessados pelo host

image e build

Use uma imagem existente:

services:
  redis:
    image: redis:8-alpine

Construa a imagem a partir de um Dockerfile:

services:
  app:
    build: .                     # Use o Dockerfile da pasta atual

Quando precisar de mais opções:

services:
  app:
    build:
      context: ./backend
      dockerfile: Dockerfile.prod
      target: prod               # Etapa alvo do build em vários estágios
      args:
        APP_VERSION: "1.4.2"
    image: myorg/app:1.4.2       # Nome da imagem após o build

Se você definir build e image, o Compose dará ao resultado do build o nome especificado em image. Depois, você poderá executar docker compose push.

ports: portas

    ports:
      - "8080:80"              # Host:contêiner
      - "127.0.0.1:5432:5432"  # Disponível apenas localmente
      - "9000-9002:9000-9002"  # Intervalo

Coloque entre aspas. O YAML pode interpretar alguns formatos número:número como números na base 60, causando erros estranhos.

expose apenas documenta a finalidade; não expõe a porta externamente. Os serviços na mesma rede já podem se comunicar entre si.

volumes: montagens

services:
  db:
    image: postgres:18
    volumes:
      - pgdata:/var/lib/postgresql                               # Volume nomeado
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro       # Bind mount (caminho relativo)

volumes:
  pgdata:
  • Os caminhos que começam com ./ ou ../ são Bind mounts relativos a compose.yaml.

  • Um nome sem caminho indica um Volume nomeado. Você precisa declará-lo na seção volumes: do nível superior. O nome real receberá o prefixo do projeto (por exemplo, shop_pgdata).

  • Para usar um Volume criado fora do Compose:

    volumes:
      pgdata:
        external: true

environment e env_file

    environment:
      NODE_ENV: production
      DATABASE_URL: postgres://app:${DB_PASSWORD}@db:5432/app
    env_file:
      - .env.app

Os valores de ${...} vêm do arquivo .env ou das variáveis de ambiente atuais. Consulte Variáveis de ambiente e arquivos .env.

depends_on e healthcheck: aguarde o banco de dados ficar realmente pronto

depends_on controla apenas a ordem de inicialização: garante que o contêiner db seja iniciado primeiro, mas não garante que o banco de dados já esteja pronto para aceitar conexões. O PostgreSQL leva alguns segundos para iniciar, e o aplicativo pode falhar por não conseguir se conectar. Com healthcheck e condition: service_healthy, o Compose aguarda a verificação de integridade ser aprovada antes de iniciar o aplicativo.

Exemplo prático (compose.yaml):

name: shop

services:
  app:
    image: postgres:18
    command: ["psql", "-h", "db", "-U", "shop", "-c", "SELECT 'app connected' AS status;"]
    environment:
      PGPASSWORD: ${DB_PASSWORD}
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:18
    environment:
      POSTGRES_USER: shop
      POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD is required}
      POSTGRES_DB: shop
    volumes:
      - pgdata:/var/lib/postgresql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -U shop -d shop"]
      interval: 2s
      timeout: 3s
      retries: 20

  adminer:
    image: adminer
    ports:
      - "8081:8080"
    profiles: [tools]

volumes:
  pgdata:

O serviço app usa o cliente do PostgreSQL para simular um aplicativo que precisa se conectar ao banco de dados ao iniciar. Defina DB_PASSWORD=change-me no arquivo .env.

Inicie apenas o db e observe a mudança do estado de integridade:

$ docker compose up -d db
[+] up 3/3
 ✔ Network shop_default Created                                                                 0.0s
 ✔ Volume shop_pgdata   Created                                                                 0.0s
 ✔ Container shop-db-1  Started                                                                 0.3s
$ docker compose ps
NAME        IMAGE         COMMAND                  SERVICE   CREATED        STATUS                                     PORTS
shop-db-1   postgres:18   "docker-entrypoint.s…"   db        1 second ago   Up Less than a second (health: starting)   127.0.0.1:5432->5432/tcp

Após alguns segundos:

$ docker compose ps
NAME        IMAGE         COMMAND                  SERVICE   CREATED          STATUS                   PORTS
shop-db-1   postgres:18   "docker-entrypoint.s…"   db        10 seconds ago   Up 9 seconds (healthy)   127.0.0.1:5432->5432/tcp

O estado muda de health: starting para healthy. Depois de encerrar tudo, inicie o app do zero:

$ docker compose up app
[+] up 3/3
 ✔ Network shop_default Created                                                                 0.0s
 ✔ Container shop-db-1  Created                                                                 0.0s
 ✔ Container shop-app-1 Created                                                                 0.1s
Attaching to app-1
Container shop-db-1 Waiting
Container shop-db-1 Healthy
app-1  |     status
app-1  | ---------------
app-1  |  app connected
app-1  | (1 row)
app-1  |
app-1 exited with code 0

w Enable Watch   d Detach

O Compose inicia primeiro o db, exibe Waiting e só inicia o app quando o estado passa a Healthy. Assim, a primeira tentativa de conexão do app é bem-sucedida (app connected). Sem condition: service_healthy, o app tentaria se conectar antes de o banco de dados estar pronto.

(A última linha, w Enable Watch d Detach, mostra os atalhos disponíveis quando o Compose está sendo executado em primeiro plano.)

A verificação de integridade usa pg_isready com -h 127.0.0.1. Sem essa opção, o banco de dados seria considerado healthy durante a inicialização inicial, e o aplicativo acabaria falhando ao se conectar. Veja os testes em PostgreSQL. O MySQL também apresenta o mesmo problema; veja MySQL.

Valores disponíveis para condition:

ValorSignificado
service_startedO contêiner foi iniciado (padrão)
service_healthyA verificação de integridade foi aprovada
service_completed_successfullyO serviço terminou a execução com código de saída 0; adequado para tarefas pontuais, como migrações de banco de dados

Você também pode adicionar restart: true em depends_on. Quando db for reiniciado, app também será reiniciado.

restart

    restart: unless-stopped

Valores disponíveis: no, always, on-failure, unless-stopped (veja o comportamento em Ciclo de vida do contêiner).

command e entrypoint

Substitua o CMD / ENTRYPOINT da imagem:

    command: ["npm", "run", "dev"]
    entrypoint: ["/app/docker-entrypoint.sh"]

networks

Por padrão, todos os serviços ficam na mesma rede nome_do_projeto_default. Para isolá-los, você pode criar redes personalizadas:

services:
  web:
    image: nginx:1.30-alpine
    networks: [frontend]
  app:
    build: .
    networks: [frontend, backend]
  db:
    image: postgres:18
    networks: [backend]

networks:
  frontend:
  backend:

web pode se conectar a app, mas não a db.

profiles: inicialização seletiva de serviços

O serviço adminer do exemplo acima tem profiles: [tools]:

$ docker compose config --services
db
app
$ docker compose --profile tools config --services
db
app
adminer

Sem especificar um profile, adminer não será iniciado. Ele só será iniciado quando você adicionar --profile tools. Isso é útil para serviços que você inicia apenas quando precisa, como ferramentas de administração e servidores de e-mail para testes.

Limites de recursos e outras configurações comuns

services:
  app:
    image: myorg/app:1.4.2
    container_name: app          # Nome fixo do contêiner (não pode ser escalado depois)
    user: "1000:1000"
    working_dir: /app
    mem_limit: 512m
    cpus: 1.5
    init: true                   # Equivale a docker run --init
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
    extra_hosts:
      - "host.docker.internal:host-gateway"

compose.override.yaml: configuração mesclada

O Compose mescla automaticamente compose.yaml e compose.override.yaml. A pasta do exemplo também contém este arquivo:

# compose.override.yaml
services:
  db:
    ports:
      - "127.0.0.1:5432:5432"

Use docker compose config para ver a configuração final mesclada (observe o ports no final):

$ docker compose config db
name: shop
services:
  db:
    environment:
      POSTGRES_DB: shop
      POSTGRES_PASSWORD: change-me
      POSTGRES_USER: shop
    image: postgres:18
    networks:
      default: null
    volumes:
      - type: volume
        source: pgdata
        target: /var/lib/postgresql
        volume: {}
    healthcheck:
      test:
        - CMD-SHELL
        - pg_isready -h 127.0.0.1 -U shop -d shop
      timeout: 3s
      interval: 2s
      retries: 20
    ports:
      - mode: ingress
        host_ip: 127.0.0.1
        target: 5432
        published: "5432"
        protocol: tcp
networks:
  default:
    name: shop_default
volumes:
  pgdata:
    name: shop_pgdata

Ao especificar explicitamente apenas um arquivo com -f compose.yaml, o override não é mesclado e ports também desaparece:

$ docker compose -f compose.yaml config db
name: shop
services:
  db:
    environment:
      POSTGRES_DB: shop
      POSTGRES_PASSWORD: change-me
      POSTGRES_USER: shop
    image: postgres:18
    networks:
      default: null
    volumes:
      - type: volume
        source: pgdata
        target: /var/lib/postgresql
        volume: {}
    healthcheck:
      test:
        - CMD-SHELL
        - pg_isready -h 127.0.0.1 -U shop -d shop
      timeout: 3s
      interval: 2s
      retries: 20
networks:
  default:
    name: shop_default
volumes:
  pgdata:
    name: shop_pgdata

docker compose config também expande a forma abreviada para o formato completo (por exemplo, pgdata:/var/lib/postgresql vira type: volume, source e target) e substitui ${DB_PASSWORD}. Quando uma configuração parece não ter efeito, o primeiro passo é usar esse comando para confirmar o que o Compose realmente leu.

Uma divisão comum:

  • compose.yaml: configurações compartilhadas por todos os ambientes.
  • compose.override.yaml: exclusivo para desenvolvimento (monta o código-fonte, abre portas para depuração) e aplicado automaticamente ao executar docker compose up localmente.
  • compose.prod.yaml: para produção, especificado com docker compose -f compose.yaml -f compose.prod.yaml up -d.

Resumo

  • image usa uma imagem existente; build constrói uma imagem a partir de um Dockerfile.
  • Só depends_on + condition: service_healthy espera até que o serviço esteja realmente pronto.
  • profiles permite iniciar serviços como ferramentas administrativas somente quando necessário.
  • compose.override.yaml é mesclado automaticamente. Use docker compose config para verificar a configuração final.

Exercícios

Exercício 1: Adicione restart: unless-stopped ao serviço db deste capítulo e limite a memória a 512 MB.

Resposta
  db:
    image: postgres:18
    restart: unless-stopped
    mem_limit: 512m

Depois da alteração, execute docker compose up -d para aplicá-la.

Exercício 2: adminer está configurado com profiles: [tools]. Como iniciá-lo junto?

Resposta
docker compose --profile tools up -d

Perguntas frequentes

Por que depends_on não espera o banco de dados ficar pronto?

Escrever apenas depends_on: [db] garante somente a ordem de inicialização. Para esperar até que o banco de dados aceite conexões, você precisa de healthcheck com condition: service_healthy. Veja como configurar a verificação de integridade neste capítulo e em [PostgreSQL](/pt/docker/postgresql/).

O que acontece se a indentação do YAML estiver errada?

docker compose config exibirá um erro, por exemplo, did not find expected key. Consulte [Mensagens de erro comuns](/pt/docker/errors/). Use apenas espaços, nunca Tab.

Ainda é necessário escrever version: '3.8'?

Não. A especificação do Compose não usa mais o campo version. As versões atuais o ignoram e exibem um aviso.

Atualizado 14 de outubro de 2026