Healthcheck no Docker Compose: como configurar direito
Um container em estado running só diz que o processo principal está vivo. Não diz que o banco aceita conexões nem que a API responde. O healthcheck cobre essa diferença: ele roda um comando periódico dentro do container e marca o serviço como healthy ou unhealthy.
Este post mostra como configurar isso no Compose e como usar o resultado para ordenar a subida dos serviços.
Premissas e limitações
Leia isto antes de copiar qualquer coisa.
- Cobertura da pesquisa insuficiente. A única fonte extraída foi a página de referência de serviços do Compose (
docs.docker.com/reference/compose-file/services/), e o texto veio truncado antes das seçõeshealthcheckedepends_on. Nada do que segue sobre esses atributos foi confirmado a partir do material coletado. - Origem do conteúdo. Os campos, os valores padrão e o comportamento descritos aqui vêm do conhecimento geral sobre o Compose, não de citação direta da documentação. Confirme cada item na seção
healthchecke na seçãodepends_onda mesma página de referência, e na referência deHEALTHCHECKdo Dockerfile. - Nada foi executado. Os exemplos abaixo não foram testados neste ambiente. Ajuste versões de imagem e comandos ao seu caso.
- Os exemplos usam
postgres:18enginx, que aparecem nos exemplos da página de referência. Os demais nomes de serviço e imagens são ilustrativos.
Os campos do healthcheck
O bloco healthcheck fica dentro de um serviço:
services:
db:
image: postgres:18
environment:
POSTGRES_USER: app
POSTGRES_DB: app
POSTGRES_PASSWORD: example
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 10s
timeout: 5s
retries: 5
start_period: 20s
O que cada campo faz:
test: o comando que decide a saúde. Exit code 0 significa saudável, 1 significa não saudável.interval: intervalo entre execuções do teste.timeout: tempo máximo que uma execução pode levar antes de contar como falha.retries: quantas falhas consecutivas são necessárias para marcar o container comounhealthy.start_period: janela inicial em que falhas não contam contraretries, para dar tempo de o serviço subir.start_interval: intervalo de teste durante ostart_period. É mais recente e depende da versão do Compose e do Engine. Verifique se seu ambiente suporta.
Pelo que conheço, os padrões são interval: 30s, timeout: 30s, retries: 3 e start_period: 0s. Confirme na documentação. Repare que 30 segundos de intervalo com 3 tentativas significa mais de um minuto até detectar uma falha, o que raramente serve para um ambiente de desenvolvimento.
As formas de test
Existem três formas, e a escolha importa:
# 1. CMD: executa o binário direto, sem shell
test: ["CMD", "pg_isready", "-U", "app"]
# 2. CMD-SHELL: passa a string para o shell do container
test: ["CMD-SHELL", "pg_isready -U app || exit 1"]
# 3. String simples: equivale a CMD-SHELL
test: pg_isready -U app
# Desativar um healthcheck herdado da imagem
test: ["NONE"]
Use CMD quando não precisar de pipe, redirecionamento ou variável de ambiente. Use CMD-SHELL quando precisar, por exemplo para expandir $POSTGRES_USER. Nesse caso, escape o cifrão no Compose ($$POSTGRES_USER), senão o Compose tenta interpolar a variável no host antes de o container ver o comando.
Exemplos por tipo de serviço
Postgres
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 5s
timeout: 3s
retries: 10
start_period: 15s
pg_isready verifica se o servidor aceita conexões. Ele não valida se o schema está pronto nem se as migrações rodaram.
Serviço HTTP
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:8080/health"]
interval: 10s
timeout: 3s
retries: 3
start_period: 30s
A flag -f faz o curl retornar erro em respostas HTTP 4xx e 5xx. Sem ela, um 500 passaria como sucesso.
Se a imagem não tiver curl, tente wget:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/health"]
Em imagens mínimas (distroless, scratch), pode não haver nenhum dos dois. Nesse caso, faça o binário da aplicação expor um subcomando de verificação, ou adicione uma ferramenta pequena à imagem.
Ordenando a subida com depends_on
O healthcheck sozinho só muda o status. Para o Compose esperar por ele, use a forma longa de depends_on:
services:
api:
build: ./api
depends_on:
db:
condition: service_healthy
db:
image: postgres:18
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER}"]
interval: 5s
timeout: 3s
retries: 10
A forma em lista (depends_on: [db]), como no exemplo do serviço proxy na página de referência, só controla a ordem de início. O material coletado não indica que ela espere o serviço ficar saudável, e pelo que conheço ela não espera. Se a API precisa do banco pronto, use condition: service_healthy.
Existem outras condições, como service_started e service_completed_successfully. Vale ler a lista completa na documentação de depends_on.
Armadilhas comuns
- Imagem sem
curlouwget. O teste falha com "executable not found" e o container ficaunhealthypara sempre. Confirme o binário comdocker compose exec <servico> which curl. start_periodcurto demais. Aplicações JVM ou com migrações pesadas passam do tempo e acumulam falhas antes de subir.- Health endpoint que só devolve 200. Um
/healthque não toca nas dependências diz que o processo está de pé, não que o serviço funciona. Decida conscientemente o que ele deve verificar. - Cifrão sem escape.
$VARé interpolado pelo Compose no host. Use$$VARpara que o shell do container resolva. timeoutmaior queinterval. Execuções se sobrepõem em cenários de lentidão e o resultado fica difícil de interpretar.- Esperar que
unhealthyreinicie o container. Por si só, o healthcheck não reinicia nada no Compose. Ele muda o status. Reinício depende derestarte do processo terminar.
Como validar
docker compose up -d
docker compose ps
docker inspect --format '{{json .State.Health}}' <container>
O docker compose ps mostra (healthy), (unhealthy) ou (health: starting) no status. O docker inspect mostra as últimas execuções, com saída e exit code, o que ajuda a entender por que um teste falha.
Próximo passo
Pegue um serviço do seu Compose que outro serviço consome, adicione um healthcheck com start_period realista e troque o depends_on em lista por condition: service_healthy. Depois, confira na documentação oficial os padrões e o suporte a start_interval na sua versão do Compose, já que esses pontos não foram verificados aqui.