Como configurar OPcache no PHP em produção pra ganhar performance
Guia técnico pra configurar OPcache PHP em produção e ganhar performance real: parâmetros, memória, validação e troubleshooting.
OPcache é a extensão de cache de bytecode embutida no PHP desde a versão 5.5. Sem ele, o PHP lê, parseia e compila cada arquivo .php em cada requisição — trabalho que se repete milhões de vezes ao dia em produção. Com OPcache habilitado e bem configurado, o bytecode compilado vive em memória compartilhada e cada request reaproveita o resultado, cortando latência e uso de CPU de forma significativa.
Este tutorial é pra desenvolvedores PHP que rodam aplicações em produção (Laravel, Symfony, WordPress, ou código próprio) e ainda não habilitaram OPcache, ou habilitaram com configuração default e não sabem se está bem dimensionado. Tempo estimado de execução: 20-30 minutos, incluindo a parte de medição e ajuste.
O foco é prático: parâmetros que importam, valores razoáveis pra começar, como verificar que o cache está sendo usado de verdade e como invalidar bytecode após deploy sem derrubar PHP-FPM. Tudo testado em PHP 8.3 sobre Ubuntu 24.04 LTS, mas os princípios valem pra qualquer versão 7.4+ em qualquer distribuição.
Pré-requisitos
Servidor Linux com PHP 7.4 ou superior, acesso sudo, PHP-FPM rodando (ou mod_php no Apache) e sua aplicação já em produção. OPcache vem embutido no PHP desde 5.5 — não precisa instalar pacote separado na maioria das distros. Tenha acesso ao php.ini ou ao diretório conf.d.
7.4 8.3 256-512 MB conf.d/10-opcache.ini Confirme que o OPcache está disponível antes de configurar. Se o comando abaixo não listar opcache, você precisa instalar o pacote php-opcache (Debian/Ubuntu) ou php-opcache (RHEL/Alma/Rocky).
php -m | grep -i opcache
Se a saída for Zend OPcache, está disponível e só falta habilitar e ajustar. Se vier vazio, instale primeiro.
Verificar o estado atual
Antes de mudar qualquer parâmetro, registre como o OPcache está agora. Isso vira sua linha de base pra comparar depois.
Liste a configuração atual do OPcache:
php --ri opcacheA saída mostra cada diretiva e o valor efetivo. Procure por opcache.enable, opcache.memory_consumption, opcache.max_accelerated_files e opcache.validate_timestamps. Se enable estiver Off, o cache não está rodando — explica boa parte da lentidão.
Veja o status em runtime via PHP-FPM (não CLI):
Crie um arquivo temporário /var/www/opcache-status.php com:
<?php
header('Content-Type: application/json');
echo json_encode(opcache_get_status(false), JSON_PRETTY_PRINT);Acesse via curl interno:
curl http://127.0.0.1/opcache-status.php | head -30Anote os valores de memory_usage.used_memory, memory_usage.free_memory, opcache_statistics.hits e opcache_statistics.misses. Se hits for zero ou muito baixo comparado a misses, o cache está mal configurado ou recém-iniciado.
Apague o arquivo depois — não deixar exposto em produção.
Configuração recomendada
Crie ou edite o arquivo de configuração específico do OPcache. Em Ubuntu/Debian fica em /etc/php/8.3/fpm/conf.d/10-opcache.ini (ajuste a versão).
Edite o arquivo de configuração:
sudo nano /etc/php/8.3/fpm/conf.d/10-opcache.iniSubstitua o conteúdo por uma configuração de produção sólida:
zend_extension=opcache.so
opcache.enable=1
opcache.enable_cli=0
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=1
opcache.revalidate_freq=60
opcache.save_comments=1
opcache.fast_shutdown=1
opcache.huge_code_pages=0
opcache.max_wasted_percentage=10
opcache.consistency_checks=0Cada parâmetro tem um motivo específico — explicação abaixo da tabela de referência.
Referência dos parâmetros principais:
| Diretiva | Valor sugerido | O que faz |
|---|---|---|
opcache.memory_consumption | 256 | Memória compartilhada (MB) pra bytecode |
opcache.interned_strings_buffer | 16 | Buffer (MB) pra strings deduplicadas |
opcache.max_accelerated_files | 20000 | Quantos arquivos podem estar cacheados |
opcache.validate_timestamps | 1 | Se 0, nunca revalida — requer reset manual |
opcache.revalidate_freq | 60 | Intervalo (s) entre verificações de mtime |
opcache.save_comments | 1 | Mantém DocBlocks — obrigatório pra Laravel/Symfony |
opcache.fast_shutdown | 1 | Desalocação mais rápida no fim do request |
Frameworks modernos (Laravel, Symfony, Doctrine) usam annotations e atributos PHP 8 que leem DocBlocks em runtime. Setar opcache.save_comments=0 quebra DI containers, validation e ORM mapping. O ganho de memória não vale o risco.
Aplique a configuração reiniciando o PHP-FPM:
sudo systemctl restart php8.3-fpmApós o restart, espere alguns segundos pra que requests reais aqueçam o cache. Em hosts com Apache + mod_php, use sudo systemctl restart apache2.
Dimensionar memory_consumption corretamente
256 MB é um ponto de partida razoável, mas a memória ideal depende do tamanho do seu codebase. Aplicação Laravel típica com vendor/ completo consome 80-150 MB. WordPress com 30 plugins pode passar de 200 MB.
Meça o consumo real após o cache aquecer (deixe a app rodar por 10-15 minutos com tráfego normal):
php -r 'print_r(opcache_get_status(false)["memory_usage"]);'A saída fica parecida com:
Array
(
[used_memory] => 142336512
[free_memory] => 125829120
[wasted_memory] => 0
[current_wasted_percentage] => 0
)Se free_memory ficar abaixo de 20% do total alocado, aumente opcache.memory_consumption pra 384 ou 512 MB. Se wasted_memory subir acima de 10%, considere ajustar opcache.max_wasted_percentage ou fazer reset programado em horários de baixo tráfego.
Frameworks orientados a objetos criam muitos nomes de classe, método e propriedade repetidos. O buffer de interned strings deduplica isso. Em apps Laravel grandes, subir de 8 MB pra 16 ou 32 MB reduz fragmentação e libera memória do bytecode principal.
Estratégia de deploy: invalidar o cache
Com validate_timestamps=1 e revalidate_freq=60, mudanças em arquivos PHP só são detectadas após 60 segundos. Pra deploys atômicos, isso é insuficiente — você precisa invalidar o cache no momento do deploy.
Instale a ferramenta cachetool pra invalidar o cache sem reiniciar PHP-FPM:
curl -sO https://gordalina.github.io/cachetool/downloads/cachetool.phar
chmod +x cachetool.phar
sudo mv cachetool.phar /usr/local/bin/cachetoolAdicione ao seu script de deploy, após o git pull ou rsync:
cachetool opcache:reset --fcgi=/var/run/php/php8.3-fpm.sockO reset é instantâneo e não derruba conexões ativas — diferente de systemctl reload.
Modo validate_timestamps=0 é mais performático (zero stat() por request), mas se você esquecer de invalidar o cache após deploy, o servidor serve código antigo indefinidamente. Só use 0 se seu deploy script tem opcache_reset() ou cachetool opcache:reset garantido.
Verificar o ganho real
Configurar sem medir é fé, não engenharia. Compare latência e CPU antes e depois.
Rode um benchmark simples com ab (Apache Bench) ou wrk contra um endpoint representativo:
ab -n 1000 -c 10 https://seudominio.com/api/produtosCompare o Time per request antes e depois de habilitar OPcache. Reduções de 30-60% em apps Laravel/Symfony são comuns. Em apps que dependem muito de I/O (banco, APIs externas), o ganho percentual é menor porque o PHP não é o gargalo.
Confirme que hits estão dominando misses:
php -r 'print_r(opcache_get_status(false)["opcache_statistics"]);'A relação hits / (hits + misses) deve passar de 99% após o cache aquecer. Se ficar abaixo de 95%, provavelmente max_accelerated_files está baixo ou o cache está sendo resetado com frequência.
Resolução de problemas
Cache fica cheio rápido e wasted_memory dispara
Sinal de que max_accelerated_files está baixo ou que max_wasted_percentage foi atingido. O OPcache começa a descartar entradas e refragmenta a memória. Aumente max_accelerated_files pra 30000-50000 e memory_consumption em 50%.
opcache_get_status() retorna false em scripts CLI
Default seguro: opcache.enable_cli=0 desabilita OPcache no CLI. Se você precisa inspecionar via CLI, suba pra 1 temporariamente. Em produção, mantenha 0 — scripts CLI são curtos e cache não ajuda.
Mudanças no código não aparecem após deploy
Ou validate_timestamps=0 sem reset programático, ou cache de outro tipo (Redis/file cache da aplicação) está servindo dados antigos. Rode cachetool opcache:reset e limpe cache de aplicação (php artisan cache:clear no Laravel, por exemplo).
Próximos passos
Depois que OPcache estiver estável, o próximo ganho costuma vir de tunar PHP-FPM (pool size, pm.max_children) pra acompanhar o throughput maior que o servidor consegue agora. Vale também avaliar OPcache JIT em apps com componente CPU-intensivo e revisar headers de cache HTTP no nginx pra reduzir ainda mais a pressão no PHP.
Se você está rodando em hardware compartilhado e o ganho de performance vira limite de recursos do plano, uma VPS Hostini com KVM dedicado dá controle total sobre PHP-FPM, memória do OPcache e parâmetros de kernel — sem disputa de CPU com vizinhos barulhentos.
Perguntas frequentes
OPcache vale a pena pra aplicações pequenas com pouco tráfego?
Sim. Mesmo com baixo RPS, OPcache elimina o trabalho repetido de parsing e compilação a cada request. O ganho percentual de latência é igual ou maior em apps pequenas — só o impacto absoluto é menor. Habilitar é praticamente grátis: alguns MB de RAM em troca de menos CPU.
Qual a diferença entre opcache.validate_timestamps=0 e validate_freq=60?
Com validate_timestamps=0 o OPcache nunca verifica se o arquivo PHP mudou — você precisa fazer reset manual após deploy. Com validate_timestamps=1 e revalidate_freq=60 ele verifica a cada 60s. O modo 0 é mais rápido mas exige pipeline de deploy que invalide o cache.
Como invalidar o OPcache após um deploy sem reiniciar PHP-FPM?
Use opcache_reset() via um script PHP acessado por CLI ou HTTP autenticado, ou cachetool (cachetool opcache:reset --fcgi=/var/run/php/php8.3-fpm.sock). Reiniciar PHP-FPM funciona mas derruba conexões em curso — o reset programático é mais limpo em produção.
Quanto de memória devo alocar pro opcache.memory_consumption?
Comece com 256 MB pra aplicações médias (Laravel, Symfony, WordPress com plugins). Monitore via opcache_get_status(): se memory.free_memory cair abaixo de 10% ou wasted_memory subir muito, aumente. Frameworks modernos como Laravel facilmente consomem 128-200 MB de cache.
OPcache JIT (PHP 8+) vale a pena em aplicações web?
Pra apps web tradicionais (Laravel, WordPress) o ganho do JIT é modesto, 5-15% em benchmarks. O JIT brilha em workloads CPU-bound: processamento de imagens, cálculos matemáticos, parsing complexo. Habilite com opcache.jit=tracing e opcache.jit_buffer_size=128M se sua app tem componente computacional pesado.
Por que opcache_get_status() retorna false mesmo com OPcache habilitado?
Geralmente é opcache.restrict_api configurado pra um path específico que não inclui o script chamador, ou opcache.enable_cli=0 ao rodar via CLI. Confira opcache.restrict_api no php --ri opcache e use opcache.enable_cli=1 pra testes via linha de comando.