Diagnóstico profundo de request timeout no cPanel webmail com Roundcube, cpsrvd e SQLite
Voltar para blog

Diagnóstico profundo de request timeout no cPanel webmail com Roundcube, cpsrvd e SQLite

07/06/2026 · 14 min · Infraestrutura

Diagnóstico profundo de Request Timeout no cPanel Webmail com Roundcube, cpsrvd e SQLite#

Neste atendimento eu tratei um cenário que, visualmente, parecia simples: o usuário acessava o Webmail do cPanel, o Roundcube carregava parcialmente e, em ações como atualização da caixa ou refresh da interface, o navegador acabava exibindo uma falha genérica de Request Timeout.

Na prática, esse tipo de erro não nasce no navegador. O navegador só mostra o sintoma final. O que eu precisei investigar foi a cadeia inteira por trás do Webmail do cPanel, envolvendo o daemon cpsrvd, o interpretador PHP interno usado pelos serviços do cPanel, conexões IMAP locais contra o Dovecot e bancos SQLite individuais do Roundcube gravados dentro da estrutura da conta.

O ponto mais importante desse diagnóstico é que eu não tratei o timeout como uma mensagem isolada. Eu tratei como um problema de sincronismo entre camadas: limite de tempo do wrapper FastCGI, espera de I/O, contenção de lock no SQLite, limite de conexão IMAP por IP local e possível throttling imposto pelo CloudLinux LVE.

Anatomia do erro no webmail cPanel#

No cPanel, o Webmail acessado nas portas 2095 e 2096 não passa pelo Apache ou LiteSpeed do site do usuário. Ele é servido pelo cpsrvd, o daemon proprietário do cPanel responsável por atender interfaces como WHM, cPanel e Webmail.

Isso muda completamente a leitura do incidente. Se um site hospedado no mesmo servidor responde normalmente, isso não prova que o Webmail esteja saudável, porque o caminho de execução é outro. O Roundcube do cPanel roda dentro da stack interna do próprio cPanel, usando os componentes instalados em /usr/local/cpanel, e o cpsrvd atua como camada de controle da requisição.

Durante o problema, o padrão observado era compatível com ações internas do Roundcube, especialmente chamadas de atualização como:

_action=refresh

Essa ação parece pequena, mas ela costuma tocar várias áreas ao mesmo tempo:

Quando uma dessas etapas fica presa, o cpsrvd não espera indefinidamente. Ele controla o tempo de vida da execução e, ao detectar estouro de tempo, interrompe o fluxo.

Origem do SIGALRM no cpsrvd#

A camada que eu considerei central nessa análise foi o comportamento do cpsrvd como wrapper de execução para o PHP interno. Em vez de olhar apenas para mensagem visual de timeout, eu considerei o mecanismo de timeout do processo.

Quando o Roundcube demora mais do que o limite esperado pelo cPanel, seja por configuração interna, seja por limite ajustado em Tweak Settings, o cpsrvd dispara um sinal de alarme no sistema operacional:

SIGALRM

Esse sinal é usado para interromper uma operação que ultrapassou o tempo permitido. No backend do cPanel, a rotina associada a esse controle costuma aparecer em torno de:

Cpanel::Server::FastCGI::_timeout("ALRM")

A leitura operacional que eu fiz foi a seguinte: o problema não era necessariamente "PHP lento" de forma genérica. O PHP podia estar bloqueado em uma chamada de I/O, aguardando retorno do SQLite ou esperando socket IMAP. Para o cpsrvd, tanto faz se a espera acontece em CPU, disco, lock ou rede local. Se a resposta não volta dentro da janela, o timer estoura e o Webmail termina em timeout.

Esse detalhe é importante porque muda a abordagem. A reação comum seria aumentar timeout e reiniciar serviço, mas isso só aumenta a janela de espera. Eu preferi identificar por que o PHP ficava pendurado antes de mexer em timeout.

Onde o PHP do Roundcube fica preso#

No Roundcube, várias operações dependem de banco de dados local. Em ambientes cPanel modernos, cada conta de e-mail pode ter um banco SQLite individual no caminho da conta, normalmente em estrutura parecida com:

/home/USUARIO/etc/dominio.com/[email protected]

Também encontrei cenários onde a análise precisava considerar o diretório por domínio:

/home/USUARIO/etc/roundcube/dominio.com/
/home/USUARIO/etc/dominio.com/

O arquivo .rcube.db guarda dados operacionais do Roundcube, como sessão, cache e índices auxiliares. Quando o Roundcube executa refresh, ele não apenas lê mensagens do IMAP, ele também atualiza estado interno.

As tabelas mais sensíveis nesse fluxo são:

session
cache
cache_index
cache_messages

Nas versões modernas do cPanel/Roundcube, eu considerei a tabela session no singular, porque isso precisa bater com o schema real confirmado via .tables. Esse detalhe evita executar manutenção em tabela errada, algo que parece pequeno, mas em produção separa uma correção limpa de uma ação inútil.

Gargalo do SQLite e lock contention#

O SQLite é excelente para bancos locais pequenos e simples, mas sua concorrência tem uma característica que pesa nesse cenário: para escrita, ele trabalha com lock no arquivo. Diferente de um MySQL ou MariaDB, onde o engine consegue lidar com concorrência de forma mais granular, o SQLite pode bloquear o banco inteiro em determinadas operações de escrita.

No caso do Roundcube, isso é crítico porque a tabela session é atualizada com frequência. Um refresh de página, uma aba adicional aberta, uma requisição AJAX atrasada ou uma operação de cache podem tentar escrever no mesmo .rcube.db em momentos próximos.

O comportamento que eu investiguei foi:

Em servidores com alto iowait, storage congestionado ou diretório /home sob latência, esse fluxo piora. O lock que deveria durar milissegundos começa a durar segundos, e segundos suficientes viram timeout na camada do cpsrvd.

Leitura em nível de kernel#

Quando o processo PHP fica bloqueado em I/O, ele pode aparecer em estado:

D

Esse estado é o Uninterruptible Sleep. Na prática, significa que o processo está esperando uma operação de kernel, muitas vezes relacionada a disco, filesystem, lock de arquivo ou chamada bloqueante. É um estado especialmente ruim para troubleshooting superficial, porque matar o processo nem sempre resolve no momento em que ele está preso aguardando o kernel devolver controle.

Para validar contenção em SQLite e lock de arquivo, eu usei como linha de raciocínio um strace focado nas syscalls relevantes:

strace -p [PID_DO_PHP] -e trace=open,fcntl,write

O objetivo não era capturar tudo. O objetivo era enxergar se o processo estava repetindo operações de abertura, escrita ou lock.

Os retornos mais importantes nessa hipótese são:

EAGAIN (Resource temporarily unavailable)
EDEADLK (Resource deadlock avoided)

Se eu vejo EAGAIN, penso em recurso temporariamente indisponível, muito compatível com tentativa de lock que não conseguiu prosseguir naquele instante. Se eu vejo EDEADLK, a leitura fica ainda mais forte para deadlock evitado pelo kernel ou pela biblioteca em cima da primitiva de lock.

Esse tipo de evidência muda a conversa com qualquer equipe. Eu deixo de dizer "o Webmail está lento" e passo a dizer "o PHP interno do cPanel está bloqueado em lock/escrita no SQLite, o cpsrvd interrompe a requisição por SIGALRM, e o timeout visual é consequência".

A ilusão do maillog com rip=::1#

Outro ponto importante foi a leitura do maillog. Em cenários de Roundcube no cPanel, é comum encontrar entradas parecidas com:

imap-login: Logged in: ... rip=::1

À primeira vista, isso parece apenas uma conexão local normal. E de fato é. O Roundcube é um cliente IMAP local e se conecta ao Dovecot pela interface de loopback, seja 127.0.0.1 em IPv4, seja ::1 em IPv6.

O problema é que, para o Dovecot, o IP ::1 representa todas as conexões originadas pelo Webmail local. Isso cria um risco de arquitetura: Connection Exhaustion.

Muitos servidores usam limites como:

mail_max_userip_connections = 10

ou:

mail_max_userip_connections = 20

Essa configuração faz sentido contra abuso externo, mas no Webmail local ela pode virar gargalo. Para o Dovecot, várias contas diferentes acessando Roundcube pelo mesmo servidor podem parecer vir do mesmo IP de origem, ::1. Se o limite for baixo, novas conexões IMAP ficam bloqueadas ou falham.

O efeito no Roundcube pode ser silencioso:

Por isso eu não ignorei o rip=::1. Ele é normal, mas também é uma pista de concentração de tráfego local.

Como eu descartei hipóteses de borda#

Antes de executar correção direta no banco, eu validei recursos básicos. Em infraestrutura, SQLite costuma falhar de formas estranhas quando não consegue criar journal, WAL ou arquivo temporário. Então eu separei a investigação por camadas.

Inodes disponíveis no /home#

O primeiro ponto foi checar inode. Um servidor pode ter espaço em disco, mas não ter mais inode livre. Para SQLite, isso é suficiente para falhar criação de arquivos auxiliares.

df -i /home

Se o uso de inode estiver em 100%, o problema não é Roundcube em si. É falta de capacidade para criar novas entradas de arquivo. Nesse cenário, o SQLite pode não conseguir criar journal, lock auxiliar ou arquivos temporários necessários para completar transação.

Quota do usuário#

Depois eu validei quota do usuário afetado:

quota -v user

Essa checagem é indispensável porque o banco .rcube.db fica dentro da área da conta. Se a conta está sem quota, o Roundcube pode autenticar, abrir interface, mas falhar quando precisa persistir sessão ou cache.

O detalhe operacional aqui é que a falha pode parecer timeout, não necessariamente erro explícito de quota. O PHP tenta escrever, o SQLite tenta concluir operação, o fluxo fica bloqueado ou falha internamente, e a camada superior mostra apenas timeout.

CloudLinux LVE throttling#

Como o ambiente pode ter CloudLinux, eu também considerei LVE. Se o usuário está sendo limitado por CPU, memória, número de processos ou entrada/saída, o Roundcube pode ser congelado ou morto antes de concluir.

Usei a leitura por usuário e período:

lveinfo --user user --period=1h --threshold=0

Na análise do resultado, eu olho com atenção para colunas de faults:

m_f
p_f

Quando m_f aparece maior que zero, considero falha de memória. Quando p_f aparece maior que zero, considero limite de processos. Em ambos os casos, o PHP pode não completar a requisição.

Esse ponto é crítico em cPanel com CloudLinux porque o problema pode parecer aplicação, mas a causa real ser isolamento de recurso. O Roundcube depende de PHP, SQLite, IMAP e filesystem dentro de limites aplicados ao usuário. Se o LVE interfere no meio, o timeout é apenas o reflexo final.

Por que o VACUUM resolveu o cenário#

A correção que eu executei teve foco no banco SQLite do Roundcube. O comando VACUUM não é apenas uma "limpeza" cosmética. Ele reconstrói o banco do zero.

Na prática, o SQLite cria uma nova cópia organizada do banco, move os dados válidos e substitui a estrutura antiga por uma versão compactada. Isso reduz fragmentação interna e reorganiza as B-Trees usadas nas tabelas.

No contexto do Roundcube, isso impacta especialmente:

session
cache
cache_index
cache_messages

Em caixas com milhares de mensagens, o cache pode crescer bastante. Um banco fragmentado de algumas dezenas de megabytes pode exigir muitas leituras para uma consulta simples. Se cada leitura depende de storage com latência, o tempo total começa a encostar no limite do cpsrvd.

Depois do VACUUM, o banco tende a ficar:

Essa é a parte que mais gosto nesse tipo de troubleshooting: a correção não é "reiniciar tudo". A correção é reduzir a causa mecânica do atraso.

Script de manutenção em lote que eu usei#

Para executar manutenção de forma controlada, eu trabalhei direto no diretório dos bancos do domínio afetado:

#!/bin/bash
# Localização dos bancos de dados individuais por conta de e-mail
DB_PATH="/home/user/etc/domain.tld"
cd $DB_PATH

for db in *.rcube.db; do
echo "Processing $db..."
# Deletamos cache e sessões antigas para reduzir o tamanho antes do vacuum
# Usamos o nome da tabela 'session' no singular conforme identificado via .tables
sqlite3 "$db" "DELETE FROM session; DELETE FROM cache; DELETE FROM cache_index; DELETE FROM cache_messages;"
sqlite3 "$db" "VACUUM;"
done

# Restauração de Ownership (Crucial após manipulação como root)
chown user:user *.rcube.db
chmod 644 *.rcube.db

Eu mantive a limpeza restrita às tabelas transitórias do Roundcube. Não mexi em mensagens de e-mail, porque as mensagens estão na Maildir, não dentro do .rcube.db. O banco guarda estado da interface, sessão e cache. Isso permite uma manutenção agressiva no cache sem apagar e-mails.

O fluxo foi:

O chown no final é obrigatório quando a manutenção é feita como root. Se eu reconstruo ou altero o banco e deixo ownership incorreto, crio outro incidente: o Roundcube volta a falhar porque não consegue escrever no próprio SQLite.

Também mantive o chmod 644 para garantir leitura/escrita do dono e leitura adequada conforme o padrão operacional aplicado naquele ambiente. Em ambientes com política diferente, eu ajustaria conforme o baseline local, mas o ponto é não terminar a manutenção deixando o banco inacessível para o contexto que o cPanel usa.

Validação com sqlite3 antes da manutenção#

Antes de assumir o nome das tabelas, o ideal é validar o schema real:

sqlite3 [email protected] ".tables"

Esse comando confirma se as tabelas existem com os nomes esperados. Eu levo isso a sério porque já vi variações de schema entre versões de Roundcube, empacotamentos e migrações antigas.

Quando confirmo session, cache, cache_index e cache_messages, a manutenção fica objetiva. Se alguma tabela não existir, o script precisa ser adaptado para não interromper todo o lote.

Em uma versão mais defensiva, eu poderia executar os deletes com checagem prévia de tabela, mas no caso operacional descrito, a identificação via .tables já tinha confirmado a estrutura.

Hipóteses alternativas que eu considerei#

Mesmo com SQLite sendo a causa mais forte, eu considerei outros cenários que podem produzir sintomas parecidos.

No WHM, a opção Cookie IP Validation pode derrubar sessões quando o usuário está em rede com IP dinâmico. Isso é comum em conexões móveis, CGNAT, 4G, 5G e links corporativos com balanceamento.

O problema acontece quando o IP do usuário muda entre uma requisição e outra:

GET inicial
POST /refresh

Se o cpsrvd considera que o IP mudou demais para a mesma sessão, ele pode invalidar cookie, gerar erro 500, redirecionar ou entrar em comportamento estranho de autenticação.

Nesse caso, o teste operacional é alterar temporariamente para modo:

Loose

em Tweak Settings, e validar se o comportamento muda. Eu não trato isso como primeira correção para SQLite, mas mantenho como hipótese quando o erro aparece apenas para usuários em redes móveis ou conexões instáveis.

cPanel-php-fpm saturado#

Outro ponto é o cpanel-php-fpm. Se ele estiver ativo para serviços do cPanel e o pool estiver saturado, novas requisições de Webmail podem entrar em fila.

O comando de status que eu usei como referência foi:

/usr/local/cpanel/scripts/restartsrv_cpanel_php_fpm --status

Se o serviço está instável, com pool saturado ou workers travados, o Roundcube pode nem chegar rapidamente na etapa de SQLite. A fila de PHP já consome a janela do cpsrvd.

Também validei a possibilidade de diretórios do Roundcube apontarem para local inexistente, mount ausente ou storage remoto com latência alta.

Um caminho como:

etc/roundcube

apontando para NFS problemático, mount removido ou destino inexistente pode transformar toda operação de sessão/cache em espera de I/O.

Nesse tipo de cenário, a investigação precisa passar por:

ls -lah /home/USUARIO/etc
find /home/USUARIO/etc -xtype l -ls
mount | grep -E 'home|nfs|roundcube'

O objetivo é confirmar se o Roundcube está escrevendo em filesystem local saudável ou se existe uma camada remota/lenta no caminho.

Como eu conectei os sintomas em uma causa operacional#

Depois de juntar as evidências, a cadeia ficou clara:

Essa leitura é muito mais útil do que apenas "Roundcube está travando". Ela permite agir na camada certa e também explicar para cliente, time interno ou recrutador o que realmente foi feito.

Resultado prático#

Depois da manutenção do SQLite com limpeza das tabelas transitórias e VACUUM, a tendência esperada foi reduzir o tempo das operações de refresh e diminuir a janela em que o banco ficava travado para escrita.

Também deixei mapeadas as verificações complementares:

df -i /home
quota -v user
lveinfo --user user --period=1h --threshold=0
/usr/local/cpanel/scripts/restartsrv_cpanel_php_fpm --status

E, quando necessário, a análise de syscall:

strace -p [PID_DO_PHP] -e trace=open,fcntl,write

O resultado operacional foi sair de um erro genérico no navegador para uma interpretação técnica completa: timeout do cpsrvd provocado por espera interna do Roundcube, com forte relação entre SQLite lock contention, cache/sessão fragmentados, possíveis limites IMAP por loopback e restrições de recurso no CloudLinux.

No fim, a mensagem Request Timeout era só o último elo da corrente. A causa real estava na sincronia entre processo, lock de arquivo, banco local e limites de serviço. Ao tratar o SQLite com VACUUM, validar inodes e quota, observar LVE e revisar o comportamento do Dovecot em ::1, eu consegui transformar um sintoma vago em um procedimento técnico reproduzível, com diagnóstico, correção e prevenção. Esse é o tipo de trabalho que considero essencial em infraestrutura: não apagar incêndio no escuro, mas entender exatamente qual camada está atrasando a entrega da requisição e reduzir o risco de recorrência antes de pensar em migração mais cara para MySQL ou MariaDB.

Este artigo foi útil?

Deixe uma reação rápida para apoiar o conteúdo:

CC BY-NC

Este post está licenciado sob CC BY-NC.

Comentários

Participe da discussão abaixo.

0 comentários