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

O que você precisa

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.

PHP mínimo 7.4
PHP recomendado 8.3
RAM extra 256-512 MB
Arquivo de config 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.

01

Liste a configuração atual do OPcache:

php --ri opcache

A 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.

02

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 -30

Anote 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).

03

Edite o arquivo de configuração:

sudo nano /etc/php/8.3/fpm/conf.d/10-opcache.ini

Substitua 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=0

Cada parâmetro tem um motivo específico — explicação abaixo da tabela de referência.

Referência dos parâmetros principais:

DiretivaValor sugeridoO que faz
opcache.memory_consumption256Memória compartilhada (MB) pra bytecode
opcache.interned_strings_buffer16Buffer (MB) pra strings deduplicadas
opcache.max_accelerated_files20000Quantos arquivos podem estar cacheados
opcache.validate_timestamps1Se 0, nunca revalida — requer reset manual
opcache.revalidate_freq60Intervalo (s) entre verificações de mtime
opcache.save_comments1Mantém DocBlocks — obrigatório pra Laravel/Symfony
opcache.fast_shutdown1Desalocação mais rápida no fim do request
save_comments precisa ficar em 1

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.

04

Aplique a configuração reiniciando o PHP-FPM:

sudo systemctl restart php8.3-fpm

Apó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.

05

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.

interned_strings_buffer importa mais do que parece

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.

06

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/cachetool

Adicione ao seu script de deploy, após o git pull ou rsync:

cachetool opcache:reset --fcgi=/var/run/php/php8.3-fpm.sock

O reset é instantâneo e não derruba conexões ativas — diferente de systemctl reload.

Não use validate_timestamps=0 sem pipeline de invalidação

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.

07

Rode um benchmark simples com ab (Apache Bench) ou wrk contra um endpoint representativo:

ab -n 1000 -c 10 https://seudominio.com/api/produtos

Compare 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.

08

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.

Tópicos:
Próximos passos Cloud Ryzen com NVMe e proteção DDoS sempre ativa.Coloque em produção numa VPS Hostini →
Esse tutorial foi útil?
Falar no WhatsApp