Deploy em produção com Docker Compose: 7 estratégias essenciais de health checks a atualizações zero-downtime
"Funciona na minha máquina" — O cemitério de contêineres em produção
O mantra de todo desenvolvedor: "Funciona na minha máquina." Mas quando os contêineres chegam à produção, o verdadeiro pesadelo começa:
- Contêineres silenciosamente OOM Killed, deixando apenas
Out of memorynos logs - Banco de dados ainda não pronto, contêineres da aplicação gritando
Connection refused - Contêiner falha às 3 da manhã sem política de reinício — serviço fora do ar até de manhã
- Arquivos de log enchendo discos,
docker logsexibindo dezenas de GB de texto não estruturado - Credenciais de produção em texto plano dentro do
docker-compose.yml, senhas de banco de dados expostas
Se você ainda usa docker compose up -d como toda a sua estratégia de produção, este artigo é para você.
Referência de conceitos essenciais
| Conceito | Finalidade | Configuração chave em produção |
|---|---|---|
| Health Check | Detectar se um contêiner está realmente pronto | healthcheck + depends_on.condition |
| Limites de recursos | Limitar CPU/memória, prevenir consumo excessivo | deploy.resources.limits |
| Política de reinício | Reinício automático em saída anormal | deploy.restart_policy |
| Secrets | Armazenamento criptografado para dados sensíveis | secrets + Docker Secret |
| Driver de logging | Logging estruturado + rotação de logs | logging.driver + logging.options |
| Profiles | Inicialização seletiva de serviços por ambiente | profiles |
| Watch | Sincronização automática de mudanças de arquivos para contêineres | watch (Compose Watch) |
5 Desafios de produção
Desafio 1: Ordem de inicialização de contêineres incontrolável
O banco de dados ainda está inicializando enquanto o contêiner da aplicação tenta se conectar, causando falhas de inicialização. depends_on apenas garante a ordem de inicialização, não a prontidão do serviço.
Desafio 2: Expansão ilimitada de recursos
Contêineres sem limites de recursos são como carros sem freios. Um único contêiner com vazamento de memória pode consumir toda a memória do host e derrubar todos os serviços.
Desafio 3: O buraco negro dos logs
O driver de log json-file padrão não faz rotação. Após 3 meses de execução, /var/lib/docker enche o disco e todos os serviços falham.
Desafio 4: Exposição de dados sensíveis
Senhas de banco de dados em blocos environment em texto plano, arquivos .env commitados no Git, chaves de API hardcoded em imagens — essas são bombas relógio para incidentes de produção.
Desafio 5: Atualizações significam tempo de inatividade
docker compose up -d para os contêineres antigos antes de iniciar os novos por padrão, tornando o serviço indisponível durante as atualizações. Para serviços 24/7, isso é inaceitável.
7 Padrões de produção
Padrão 1: Health checks e ordenação de dependências
Problema: depends_on apenas controla a ordem de inicialização, não garante a prontidão do serviço.
Solução: Usar healthcheck + depends_on.condition: service_healthy.
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: appdb
POSTGRES_USER: appuser
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
interval: 5s
timeout: 3s
retries: 5
start_period: 10s
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
app:
image: myapp:latest
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
ports:
- "3000:3000"
Parâmetros chave:
interval: Intervalo de verificação, recomendado 5-10 segundos para produçãotimeout: Timeout de uma única verificação, recomendado 3-5 segundosretries: Falhas consecutivas antes de marcar como unhealthystart_period: Período de carência após o início do contêiner, falhas não contam para os retries
Padrão 2: Limites de recursos e proteção OOM
Problema: Contêineres sem limites competem pelos recursos do host. Um contêiner descontrolado pode derrubar toda a máquina.
Solução: Usar deploy.resources para definir limites e reservas de CPU e memória.
services:
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: '2.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 10s
timeout: 5s
retries: 3
worker:
image: myworker:latest
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
memory: 512M
Limites vs Reservas:
limits: Teto rígido, se excedido → OOM Kill ou throttling de CPUreservations: Garantia flexível, o escalonador tenta atender mas não impõe
Estratégia de proteção OOM:
services:
critical-service:
image: critical-app:latest
deploy:
resources:
limits:
memory: 1G
cap_add:
- SYS_PTRACE
No nível do host, configure vm.overcommit_memory e ajuste as políticas OOM:
# Verificar OOM Score do contêiner
docker inspect --format='{{.State.OOMKilled}}' <container_id>
# Definir política OOM do host: não matar processos críticos
echo -1000 > /proc/<pid>/oom_score_adj
Padrão 3: Logging estruturado e rotação de logs
Problema: O driver de log json-file padrão não faz rotação; discos enchem com o tempo.
Solução: Configurar driver de log + política de rotação. O driver local é recomendado para produção.
services:
app:
image: myapp:latest
logging:
driver: local
options:
max-size: "10m"
max-file: "5"
tag: "{{.Name}}/{{.ID}}"
nginx:
image: nginx:1.27-alpine
logging:
driver: json-file
options:
max-size: "50m"
max-file: "10"
tag: "nginx/{{.Name}}"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
Comparação de drivers de log:
| Driver | Caso de uso | Vantagens | Desvantagens |
|---|---|---|---|
| local | Padrão para produção | Auto-rotação, armazenamento comprimido | Apenas local |
| json-file | Logs JSON estruturados necessários | Suporte nativo do Docker | Necessita configuração manual de rotação |
| syslog | Coleta centralizada de logs | Pode enviar para remoto | Configuração complexa |
| fluentd | Integração com stack EFK | Roteamento flexível de logs | Requer implantação do Fluentd |
Logging estruturado em nível de aplicação:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
ENV TZ=UTC
ENV LOG_FORMAT=json
CMD ["node", "server.js"]
const logger = {
info: (msg, meta = {}) => {
console.log(JSON.stringify({ level: 'info', msg, ts: new Date().toISOString(), ...meta }));
},
error: (msg, meta = {}) => {
console.error(JSON.stringify({ level: 'error', msg, ts: new Date().toISOString(), ...meta }));
}
};
Padrão 4: Gerenciamento de Secrets
Problema: Dados sensíveis armazenados em texto plano em arquivos compose ou variáveis de ambiente.
Solução: Docker Secrets + variáveis de ambiente com sufixo _FILE.
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
file: ./secrets/api_key.txt
jwt_secret:
file: ./secrets/jwt_secret.txt
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: appdb
POSTGRES_USER: appuser
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
app:
image: myapp:latest
environment:
DATABASE_URL: postgresql://appuser:${DB_PASSWORD}@postgres:5432/appdb
API_KEY_FILE: /run/secrets/api_key
JWT_SECRET_FILE: /run/secrets/jwt_secret
secrets:
- api_key
- jwt_secret
Gerenciamento de arquivos de secrets:
# Criar diretório de secrets
mkdir -p secrets
chmod 700 secrets
# Escrever arquivos de secrets
echo "my-super-secret-password-2026" > secrets/db_password.txt
echo "ak-live-xxxx-yyyy-zzzz" > secrets/api_key.txt
echo "jwt-hs256-secret-key-here" > secrets/jwt_secret.txt
# Definir permissões: legível apenas pelo root
chmod 600 secrets/*.txt
.gitignore deve incluir:
secrets/
*.secret
.env.production
.env.staging
Comparação Docker Secrets vs .env:
| Recurso | Docker Secrets | Arquivos .env |
|---|---|---|
| Armazenamento criptografado | Sim (no modo Swarm) | Não |
| Permissões de arquivo | Restritas (/run/secrets/) | Depende do filesystem |
| Trilha de auditoria | Sim | Não |
| Sincronização entre nós | Swarm sincroniza automaticamente | Distribuição manual |
| Caso de uso | Swarm/produção de host único | Desenvolvimento |
Padrão 5: Atualizações rolling zero-downtime
Problema: docker compose up -d para os contêineres antigos antes de iniciar os novos por padrão.
Solução: Usar docker compose up --no-down + health checks + proxy reverso.
services:
app:
image: myapp:${APP_VERSION:-latest}
deploy:
replicas: 2
update_config:
parallelism: 1
delay: 10s
order: start-first
failure_action: rollback
rollback_config:
parallelism: 0
order: stop-first
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 5s
timeout: 3s
retries: 3
start_period: 15s
labels:
- "com.toolsku.app=true"
ports:
- "3000-3001:3000"
nginx:
image: nginx:1.27-alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
app:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/health"]
interval: 10s
timeout: 3s
retries: 3
Configuração de proxy reverso Nginx:
upstream app_backend {
server app:3000;
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://app_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_next_upstream error timeout http_502 http_503;
}
location /health {
access_log off;
return 200 'ok';
add_header Content-Type text/plain;
}
}
Script de atualização zero-downtime:
#!/bin/bash
set -euo pipefail
NEW_IMAGE="myapp:v2.0.0"
echo "🚀 Iniciando atualização zero-downtime para ${NEW_IMAGE}"
# 1. Baixar nova imagem
docker compose pull app
# 2. Iniciar novos contêineres (sem parar os antigos)
docker compose up -d --no-deps --scale app=2 app
# 3. Aguardar novos contêineres ficarem saudáveis
echo "⏳ Aguardando novos contêineres ficarem saudáveis..."
sleep 15
# 4. Verificar saúde do novo contêiner
for i in $(seq 1 30); do
if curl -sf http://localhost:3000/health > /dev/null 2>&1; then
echo "✅ Novo contêiner está saudável"
break
fi
if [ $i -eq 30 ]; then
echo "❌ Health check falhou, revertendo..."
docker compose up -d --no-deps --scale app=1 app
exit 1
fi
sleep 2
done
# 5. Reduzir para 1 réplica
docker compose up -d --no-deps --scale app=1 app
echo "🎉 Atualização concluída com sucesso"
Padrão 6: Stack de monitoramento Prometheus + Grafana
Problema: Executar produção sem monitoramento é voar às cegas — você só fica sabendo dos problemas pelas reclamações dos usuários.
Solução: Implantar um stack completo de monitoramento Prometheus + Grafana.
services:
prometheus:
image: prom/prometheus:v2.52.0
container_name: prometheus
restart: unless-stopped
ports:
- "9090:9090"
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=30d'
- '--storage.tsdb.retention.size=5GB'
deploy:
resources:
limits:
memory: 512M
networks:
- monitoring
grafana:
image: grafana/grafana:11.0.0
container_name: grafana
restart: unless-stopped
ports:
- "3001:3000"
environment:
GF_SECURITY_ADMIN_USER: admin
GF_SECURITY_ADMIN_PASSWORD_FILE: /run/secrets/grafana_password
secrets:
- grafana_password
volumes:
- grafana_data:/var/lib/grafana
deploy:
resources:
limits:
memory: 256M
depends_on:
- prometheus
networks:
- monitoring
node-exporter:
image: prom/node-exporter:v1.8.0
container_name: node-exporter
restart: unless-stopped
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--path.rootfs=/rootfs'
networks:
- monitoring
alertmanager:
image: prom/alertmanager:v0.27.0
container_name: alertmanager
restart: unless-stopped
ports:
- "9093:9093"
volumes:
- ./monitoring/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
networks:
- monitoring
secrets:
grafana_password:
file: ./secrets/grafana_password.txt
volumes:
prometheus_data:
grafana_data:
networks:
monitoring:
driver: bridge
Configuração do Prometheus:
# monitoring/prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "alert_rules.yml"
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
- job_name: 'app'
static_configs:
- targets: ['app:3000']
metrics_path: /metrics
Regras de alertas:
# monitoring/alert_rules.yml
groups:
- name: container_alerts
rules:
- alert: ContainerDown
expr: up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Container {{ $labels.instance }} is down"
- alert: HighMemoryUsage
expr: process_resident_memory_bytes / (1024 * 1024) > 400
for: 5m
labels:
severity: warning
annotations:
summary: "Memory usage exceeds 400MB on {{ $labels.instance }}"
- alert: DiskSpaceLow
expr: node_filesystem_avail_bytes / node_filesystem_size_bytes < 0.1
for: 5m
labels:
severity: critical
annotations:
summary: "Disk space below 10% on {{ $labels.instance }}"
Padrão 7: Configuração multi-ambiente (dev/staging/prod)
Problema: Configs de dev, staging e produção estão misturadas — alterar a config de um ambiente corre o risco de afetar outros.
Solução: Usar docker-compose.override.yml + estratégia de sobreposição multi-arquivo.
Estrutura de diretórios:
project/
├── docker-compose.yml # Configuração base
├── docker-compose.override.yml # Sobrescrita de dev (carregada automaticamente)
├── docker-compose.staging.yml # Sobrescrita de staging
├── docker-compose.prod.yml # Sobrescrita de produção
├── .env # Variáveis de ambiente padrão
├── .env.staging # Variáveis de ambiente de staging
├── .env.prod # Variáveis de ambiente de produção
├── monitoring/
│ ├── prometheus.yml
│ └── alertmanager.yml
└── secrets/
├── db_password.txt
├── api_key.txt
└── grafana_password.txt
Configuração base docker-compose.yml:
services:
app:
image: myapp:${APP_VERSION:-latest}
environment:
NODE_ENV: ${NODE_ENV:-development}
DATABASE_URL: postgresql://appuser:${DB_PASSWORD}@postgres:5432/appdb
REDIS_URL: redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- app-network
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: appdb
POSTGRES_USER: appuser
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
interval: 5s
timeout: 3s
retries: 5
networks:
- app-network
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
networks:
- app-network
secrets:
db_password:
file: ./secrets/db_password.txt
volumes:
postgres_data:
networks:
app-network:
driver: bridge
Sobrescrita de dev docker-compose.override.yml (carregada automaticamente):
services:
app:
build: .
volumes:
- .:/app
- /app/node_modules
ports:
- "3000:3000"
- "9229:9229"
environment:
NODE_ENV: development
LOG_LEVEL: debug
deploy:
resources:
limits:
memory: 512M
command: node --inspect=0.0.0.0:9229 server.js
adminer:
image: adminer:latest
ports:
- "8080:8080"
networks:
- app-network
Sobrescrita de produção docker-compose.prod.yml:
services:
app:
image: myapp:${APP_VERSION}
ports:
- "3000:3000"
environment:
NODE_ENV: production
LOG_LEVEL: info
deploy:
replicas: 2
resources:
limits:
cpus: '2.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
logging:
driver: local
options:
max-size: "10m"
max-file: "5"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 15s
postgres:
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
memory: 1G
logging:
driver: local
options:
max-size: "50m"
max-file: "10"
redis:
deploy:
resources:
limits:
memory: 512M
command: redis-server --appendonly yes --maxmemory 400mb --maxmemory-policy allkeys-lru
volumes:
- redis_data:/data
logging:
driver: local
options:
max-size: "10m"
max-file: "3"
nginx:
image: nginx:1.27-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.prod.conf:/etc/nginx/nginx.conf:ro
depends_on:
app:
condition: service_healthy
deploy:
resources:
limits:
memory: 128M
logging:
driver: local
options:
max-size: "50m"
max-file: "10"
volumes:
redis_data:
Comandos de inicialização:
# Desenvolvimento (carrega override automaticamente)
docker compose up -d
# Staging
docker compose -f docker-compose.yml -f docker-compose.staging.yml --env-file .env.staging up -d
# Produção
docker compose -f docker-compose.yml -f docker-compose.prod.yml --env-file .env.prod up -d
5 Armadilhas comuns
Armadilha 1: depends_on não significa serviço pronto
❌ Errado:
services:
app:
depends_on:
- postgres
# Contêiner postgres iniciou, mas o BD pode não estar inicializado ainda
✅ Correto:
services:
app:
depends_on:
postgres:
condition: service_healthy
postgres:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
interval: 5s
timeout: 3s
retries: 5
start_period: 10s
Armadilha 2: Sem limites de recursos
❌ Errado:
services:
app:
image: myapp:latest
# Sem limites de recursos — um vazamento de memória pode consumir todo o host
✅ Correto:
services:
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: '2.0'
memory: 512M
reservations:
cpus: '0.25'
memory: 128M
Armadilha 3: Sem rotação de logs
❌ Errado:
services:
app:
image: myapp:latest
# Driver json-file padrão, logs crescem indefinidamente
✅ Correto:
services:
app:
image: myapp:latest
logging:
driver: local
options:
max-size: "10m"
max-file: "5"
Armadilha 4: Dados sensíveis em texto plano
❌ Errado:
services:
postgres:
environment:
POSTGRES_PASSWORD: "my-secret-password-123"
# Senha em texto plano no arquivo compose — desastre se commitado no Git
✅ Correto:
services:
postgres:
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
Armadilha 5: Usar a tag latest
❌ Errado:
services:
app:
image: myapp:latest
# Cada pull pode obter uma imagem diferente — não é reprodutível
✅ Correto:
services:
app:
image: myapp:2.1.0
# Ou usar uma variável para controle de versão
image: myapp:${APP_VERSION:-2.1.0}
Referência de solução de erros
| Mensagem de erro | Causa | Solução |
|---|---|---|
OOMKilled |
Contêiner excedeu o limite de memória | Aumentar limite memory ou otimizar uso de memória da app |
Connection refused |
Serviço dependente não pronto | Adicionar healthcheck + depends_on.condition |
no space left on device |
Logs/imagens enchendo o disco | Configurar rotação logging + docker system prune |
Loop de restarting |
App falha na inicialização | Verificar docker logs <id> e confirmar configuração |
permission denied |
Problema de permissão de arquivo/diretório | Verificar diretiva user e permissões de volumes |
port is already allocated |
Conflito de portas | Alterar mapeamento de portas ou parar processo em conflito |
Status unhealthy |
Health check falhando | Verificar se o comando healthcheck está correto |
secret not found |
Arquivo de secret ausente | Garantir que o arquivo correspondente existe em secrets/ |
Cannot connect to the Docker daemon |
Docker não está em execução | systemctl start docker |
image pulling failed |
Falha ao baixar imagem | Verificar rede/autenticação do registry/grafia do nome da imagem |
Otimização avançada
Docker Compose Watch para desenvolvimento
Compose Watch sincroniza automaticamente mudanças de arquivos para os contêineres sem reconstruir imagens:
services:
app:
build: .
develop:
watch:
- action: sync
path: ./src
target: /app/src
- action: rebuild
path: ./package.json
- action: sync+restart
path: ./config
target: /app/config
# Iniciar modo watch
docker compose watch
Isolamento de rede e segurança
services:
app:
networks:
- frontend
- backend
postgres:
networks:
- backend
# postgres não está na rede frontend — acesso externo bloqueado
nginx:
networks:
- frontend
ports:
- "80:80"
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true
# internal:true desabilita acesso externo
Otimização de imagens com multi-stage builds
# ---- Estágio de build ----
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# ---- Estágio de runtime ----
FROM node:20-alpine AS runner
WORKDIR /app
RUN addgroup -g 1001 -S appgroup && \
adduser -S appuser -u 1001 -G appgroup
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
CMD ["node", "dist/server.js"]
Comparação de ferramentas de orquestração
| Recurso | Docker Compose | Kubernetes | Nomad | Docker Swarm |
|---|---|---|---|---|
| Complexidade | Baixa | Alta | Média | Baixa |
| Deploy de host único | ✅ Excelente | ❌ Exagero | ⚠️ Usável | ✅ Excelente |
| Orquestração multi-host | ❌ Não suportado | ✅ Capacidade central | ✅ Capacidade central | ⚠️ Básico |
| Auto-scaling | ❌ | ✅ HPA/VPA | ✅ | ⚠️ Manual |
| Atualizações rolling | ⚠️ Requer scripts | ✅ Nativo | ✅ Nativo | ✅ Nativo |
| Service discovery | ⚠️ DNS | ✅ CoreDNS | ✅ Consul | ✅ DNS |
| Orquestração de armazenamento | ❌ | ✅ CSI | ✅ CSI | ⚠️ Básico |
| Curva de aprendizado | Baixa | Alta | Média | Baixa |
| Escala adequada | 1-10 serviços | 100+ serviços | 50+ serviços | 10-50 serviços |
| Pronto para produção | ✅ Host único | ✅ Grande escala | ✅ Média a grande escala | ⚠️ Comunidade em declínio |
Recomendação: Use Docker Compose para produção de host único/pequena escala, Kubernetes para grande escala, Nomad se você está no ecossistema HashiCorp. Docker Swarm está cada vez mais marginalizado — não recomendado para novos projetos.
Resumo
O deploy em produção com Docker Compose não é apenas
docker compose up -d. Health checks garantem que os serviços estão realmente prontos, limites de recursos previnem avalanches OOM, rotação de logs evita esgotamento de disco, Secrets protegem dados sensíveis, atualizações zero-downtime garantem disponibilidade 24/7, stacks de monitoramento eliminam pontos cegos, e configurações multi-ambiente mantêm dev/staging/prod devidamente separados. Domine essas 7 estratégias, e Docker Compose é totalmente capaz de deploys de produção em pequena e média escala.
Ferramentas recomendadas
- Formatador JSON - Formatar configurações JSON relacionadas ao Docker Compose YAML
- Codificação Base64 - Codificar Secrets e dados de configuração sensíveis
- Calculadora de Hash - Gerar checksums para arquivos de configuração e garantir consistência nos deploys
Experimente estas ferramentas executadas localmente no navegador — nenhum cadastro necessário →