Guia: checklist técnico para preparar VPS Debian com Docker Compose para aplicações PHP em produção
Por Kevin Marcondes · Publicado em · 9 min de leitura
guia: checklist técnico para preparar VPS Debian com Docker Compose para aplicações PHP em produção. Este passo a passo cobre sysctl, ulimits, journald, swap e cgroups. Você aplica os ajustes, valida com comandos simples e reduz riscos de OOM, perda de logs e gargalos de rede ou descritores de arquivo.
O que este checklist cobre e quando usar
O checklist foca na preparação do host Debian e do Docker para monólitos PHP ou stacks PHP em Compose. Ele prioriza estabilidade, previsibilidade e observabilidade antes de otimizações finas.
Use ao iniciar uma VPS, migrar hosts ou padronizar ambientes. Também serve para reduzir incidentes por OOM, logs sem retenção e limites do sistema.
Aplique primeiro em staging. Mudanças em kernel, cgroups e no serviço do Docker podem afetar todos os containers em execução.
- Host Debian 11/12 com systemd e Docker instalado
- Docker Compose v2 (CLI docker compose)
- Stacks PHP com serviços separados (web, php‑fpm, banco, cache)
Pré-requisitos e versões recomendadas
A combinação de kernel, systemd e Docker define cgroups, logs e limites. Em Debian recente, cgroup v2 é padrão e o Docker usa driver de cgroup systemd.
Confira estes pontos antes de seguir. Garanta espaço em /var para o journald e privilégios de root.
- Kernel e systemd atualizados com cgroup v2
- Docker Engine 20.10+ e Compose v2
- Acesso root ao host e espaço livre em /var
# Verificar cgroup v2
stat -fc %T /sys/fs/cgroup # esperado: cgroup2fs
# Verificar driver de cgroup do Docker
docker info | grep -i 'Cgroup Driver' # esperado: systemd
# Versões
docker --version
docker compose version
Quais sysctl ajustar no Debian para Docker/PHP?
Alguns parâmetros do kernel evitam saturação de filas, esgotamento de portas efêmeras e limites de conexões. Configure via /etc/sysctl.d para persistência e carregue sem reboot.
Se notar intermitência na bridge do Docker, investigue rede e filas. Veja causas e correções em conexão cai na Docker bridge.
Os valores a seguir são ponto de partida. Ajuste conforme memória, tráfego e padrão de conexões da sua aplicação.
- Habilitar roteamento para redes Docker
- Ampliar backlog e SYN backlog
- Expandir portas efêmeras para muitas conexões de saída
- Dimensionar conntrack à carga e memória
# /etc/sysctl.d/99-docker-php.conf
net.ipv4.ip_forward = 1
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 4096
net.ipv4.ip_local_port_range = 1024 65535
# Conntrack: comece moderado e ajuste com métricas
net.netfilter.nf_conntrack_max = 131072
# Aplicar agora
sysctl --system
# Checar
sysctl -n net.ipv4.ip_forward net.core.somaxconn net.ipv4.tcp_max_syn_backlog net.ipv4.ip_local_port_range net.netfilter.nf_conntrack_max
Como configurar ulimits no host e nos containers?
Limites de arquivos e processos evitam erros como "Too many open files". Em serviços gerenciados pelo systemd, use overrides. Em containers, defina ulimits no Compose.
Garanta que o limite do host seja maior que o do container. Caso contrário, o Docker não aplicará o valor solicitado. Reiniciar o serviço docker interrompe containers em execução. Para diagnóstico de FDs no PHP‑FPM, veja como resolver erro de open files.
- Aumente fs.file-max e LimitNOFILE no docker.service
- Defina ulimits por serviço no Compose
- Alinhe limites com o PHP‑FPM (pm.max_children, sockets, conexões)
# 1) Limite global de FDs do kernel
sysctl -w fs.file-max=500000
# Para persistir: adicione fs.file-max = 500000 em /etc/sysctl.d/99-docker-php.conf
# 2) Override do systemd para o Docker
mkdir -p /etc/systemd/system/docker.service.d
cat >/etc/systemd/system/docker.service.d/override.conf <<'EOF'
[Service]
LimitNOFILE=1048576
LimitNPROC=131072
EOF
systemctl daemon-reload && systemctl restart docker
# 3) docker-compose.yml (exemplo por serviço)
# services:
# php:
# image: your-php-image:latest
# ulimits:
# nofile: 65536
# nproc: 4096
# core: 0
# Diagnóstico de FDs em runtime
# docker compose exec php sh -lc 'ulimit -n; ulimit -u'
Journald: retenção, tags e impacto em disco
Usar journald como log-driver centraliza logs e facilita retenção com compressão. Defina cotas e retenção para evitar saturar o disco da VPS.
Padronize a tag dos containers para filtros consistentes. Para detalhes de rotação e compactação em VPS com pouco disco, veja o tutorial de retenção de logs com journald + logrotate + rsyslog.
- Defina Storage=persistent para manter logs após reboot
- Limite SystemMaxUse e MaxRetentionSec
- Use log-driver=journald com tag consistente
# /etc/systemd/journald.conf (trecho)
[Journal]
Storage=persistent
SystemMaxUse=1G
SystemMaxFileSize=128M
MaxRetentionSec=14day
RateLimitIntervalSec=30s
RateLimitBurst=10000
systemctl restart systemd-journald
journalctl --disk-usage
# /etc/docker/daemon.json (trecho)
{
"log-driver": "journald",
"log-opts": {"tag": "{{.Name}}"},
"exec-opts": ["native.cgroupdriver=systemd"]
}
systemctl restart docker && sleep 2 && docker info | grep -i log
Swap e OOM: qual a estratégia segura para produção?
Uma pequena área de swap reduz OOM kills em picos curtos de memória. Em workloads PHP, mantenha swappiness baixo e monitore page faults.
Crie swapfile se faltar swap e valide. Swap em disco lento pode aumentar a latência. Para entender picos noturnos de I/O e swap, veja como resolver picos de I/O e swap em VPS.
- Crie swapfile proporcional à RAM e ao tráfego
- Ajuste vm.swappiness para 10–20 como ponto de partida
- Alinhe memswap ao mem_limit em containers críticos
# Criar swapfile (ex.: 2G)
fallocate -l 2G /swapfile || dd if=/dev/zero of=/swapfile bs=1M count=2048
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
# Reduzir swappiness
sysctl -w vm.swappiness=10
# Persistir em /etc/sysctl.d/99-docker-php.conf
# vm.swappiness = 10
# Conferir swap
swapon --show
cat /proc/sys/vm/swappiness
# Ajuste por container no compose (evita uso de swap pelo container)
# services:
# php:
# mem_limit: 1g
# memswap_limit: 1g # total = memória + swap; igual à mem_limit ≈ sem swap no container
Cgroups v2 e Docker Compose: como limitar recursos?
Cgroups v2 expõe limites de CPU, memória, PIDs e I/O. No Compose local, a chave deploy.* é ignorada; use campos específicos de limites.
Valide o driver de cgroup systemd e confira os limites via docker inspect e arquivos em /sys/fs/cgroup. Para um passo a passo completo, veja cgroup v2 + systemd ResourceControl no Docker.
- Use mem_limit, memswap_limit, cpus e pids_limit no Compose
- Ajuste blkio_config para não sufocar o disco
- Evite deploy.resources no Compose sem Swarm
# docker-compose.yml (exemplo simplificado)
version: "3.9"
services:
php:
image: your-php-image:latest
cpus: "1.5"
mem_limit: 1g
memswap_limit: 1g
pids_limit: 4096
ulimits:
nofile: 65536
nproc: 4096
blkio_config:
weight: 600
# Verificações
# docker compose up -d
# docker inspect --format '{{json .HostConfig.NanoCpus}}' <container>
# docker compose exec php sh -lc 'cat /sys/fs/cgroup/memory.max 2>/dev/null || cat /sys/fs/cgroup/memory.limit_in_bytes'
Verificações rápidas e automação do checklist
Depois dos ajustes, execute verificações objetivas. Elas confirmam sysctl, limites, logs e cgroups de forma repetível.
Automatize com Ansible ou scripts. Se um valor divergir, corrija nos arquivos persistentes: sysctl.d, daemon.json e overrides do systemd.
| Item | Comando | Saída esperada |
|---|---|---|
| Cgroup v2 | stat -fc %T /sys/fs/cgroup | cgroup2fs |
| Driver de cgroup do Docker | docker info | grep -i 'Cgroup Driver' | systemd |
| Portas efêmeras | sysctl -n net.ipv4.ip_local_port_range | 1024 65535 |
| Backlogs (somaxconn/SYN) | sysctl -n net.core.somaxconn net.ipv4.tcp_max_syn_backlog | >= 1024 / >= 1024 |
| Conntrack | sysctl -n net.netfilter.nf_conntrack_max | > 65536 (ajustar à carga) |
| Swap e swappiness | swapon --show; cat /proc/sys/vm/swappiness | swap ativo; 10–20 |
| Journald retenção | journalctl --disk-usage | <= cota definida (ex.: ~1G) |
| Limite de FDs (host) | cat /proc/sys/fs/file-max | >= 500000 (ajuste conforme) |
| Limite de FDs (container) | docker compose exec php sh -lc 'ulimit -n' | >= 65536 |
# Validação resumida
set -euo pipefail
printf "Cgroup: "; stat -fc %T /sys/fs/cgroup
printf "Cgroup Driver: "; docker info 2>/dev/null | awk -F: '/Cgroup Driver/{print $2}'
printf "Ports: "; sysctl -n net.ipv4.ip_local_port_range
printf "Backlogs: "; sysctl -n net.core.somaxconn net.ipv4.tcp_max_syn_backlog
printf "Conntrack: "; sysctl -n net.netfilter.nf_conntrack_max
printf "Swap: "; swapon --show | tail -n +2 | wc -l; cat /proc/sys/vm/swappiness
journalctl --disk-usage || true
printf "FDs host: "; cat /proc/sys/fs/file-max
# FDs container (requer serviço 'php')
docker compose exec -T php sh -lc 'echo -n "FDs container: "; ulimit -n' || true
Perguntas frequentes
Qual swappiness usar em produção para PHP em Docker?
Use swappiness baixo, entre 10 e 20, para reduzir trocas agressivas e latência. Mantenha uma pequena área de swap para amortecer picos curtos de memória. Monitore OOM kills e page faults e ajuste ao perfil de tráfego e à RAM do host.
É melhor usar journald ou json-file para logs de containers?
Para VPS, journald facilita retenção e compressão com o mesmo mecanismo do host. Ele evita crescimento descontrolado do json-file. Se usar json-file, aplique max-size e max-file. Com journald, defina SystemMaxUse e retenção e monitore com journalctl --disk-usage.
O deploy.resources do Compose funciona fora do Swarm?
Não. O deploy.resources é ignorado no Compose tradicional. Para impor limites, use cpus, mem_limit, memswap_limit, pids_limit, blkio_config e ulimits. Valide com docker inspect e lendo arquivos em /sys/fs/cgroup dentro do container.
Qual valor de nofile devo definir para PHP‑FPM?
Comece com 65536 por container PHP e ajuste com métricas. Considere conexões simultâneas, sockets, arquivos e logs. No host, mantenha fs.file-max e LimitNOFILE bem acima dos valores dos containers. Meça o pico real de FDs em horários críticos.
Como evitar que logs ocupem todo o disco da VPS?
Limite o journald com SystemMaxUse e MaxRetentionSec e padronize tags. Se usar json-file, configure max-size e max-file. Monitore com journalctl --disk-usage e crie alertas antes de 80% do disco. Limpezas periódicas de imagens e volumes também ajudam.
Preciso desativar completamente a swap em produção?
Geralmente, não. Uma pequena swap ajuda a evitar OOM kills em picos breves. Desativar totalmente aumenta o risco de reinícios de processos. Prefira swappiness baixo e limites claros por container (mem_limit e memswap_limit) para controlar consumo sem sufocar o host.
Quer revisar seu checklist antes do deploy?
Envie seu docker-compose.yml e as saídas das verificações. Podemos revisar limites, sysctl e journald para um início mais tranquilo.
Conversar sobre o checklistVeja também
- Guia prático: como configurar failover ativo-passivo entre duas VPS com Keepalived, HAProxy e lsyncd para uploads sincronizados
- Guia prático para dimensionamento de VPS e tuning de PHP‑FPM para lojas Magento/OpenMage com picos sazonais: do sizing ao teste de carga
- Guia: como planejar e executar testes de carga em aplicações PHP monolito usando k6 e Docker Compose — passo a passo
- Guia prático para implementar retries exponenciais e circuit breaker em integrações entre ERP e marketplaces