Do Zero ao VS Code: Como domei o cPanel no Docker e criei um ambiente de Dev em tempo real
Subir cPanel dentro de container não é tarefa “aperta play”. O stack mistura requisitos de sistema, comportamento de filesystem e bootstrap sensível de serviços. Meu objetivo era direto: editar core/plugin do cPanel no VS Code do host e refletir em tempo real no container, sem ciclo manual de cópia.
Nesse artigo eu registro exatamente a execução de ponta a ponta: erro real, hipótese, validação, correção e arquitetura final que ficou estável.
Objetivo operacional
- rodar cPanel/WHM em AlmaLinux 8 no Docker para laboratório de desenvolvimento;
- manter persistência real do core do cPanel entre recriações;
- habilitar ponte host-container para edição direta no VS Code;
- evitar solução frágil baseada em cópia manual (
docker cp) e “gambiarras” de sessão.
Passo 1: overlay2 travando remoção de container
O primeiro bloqueio apareceu no reset do ambiente:
Error response from daemon: container [HASH]: driver "overlay2" failed to remove root filesystem: unlinkat /var/lib/docker/overlay2/[HASH]/diff/usr/local/cpanel/logs/dnsadmin_log: operation not permitted
Diagnóstico aplicado
Esse padrão aponta para atributo especial no arquivo alvo (imutável +i ou append-only +a) e/ou lock residual no layer do overlay2. O Docker tenta remover a camada, o kernel nega o unlinkat, e o container vira “zumbi” no teardown.
Correção executada no host
- Parei o daemon para liberar locks de camada.
systemctl stop docker
- Limpei atributos extras no arquivo e diretório afetado.
chattr -e -i -a /var/lib/docker/overlay2/[HASH_DA_CAMADA]/diff/usr/local/cpanel/logs/dnsadmin_log
chattr -R -i /var/lib/docker/overlay2/[HASH_DA_CAMADA]/diff/usr/local/cpanel/logs/
- Removi artefatos residuais do container no filesystem local.
rm -rf /var/lib/docker/containers/[HASH_DO_CONTAINER]*
- Subi o Docker novamente.
systemctl start docker
Com isso, limpei a base para um provisionamento limpo.
Passo 2: conflito de boot com /etc/fstab
No segundo ciclo, a instalação do cPanel quebrava por ausência de /etc/fstab, mesmo com touch no command do Compose.
Causa raiz
Eu estava montando volume sobre /etc. Em timing de inicialização, o mount podia sobrepor o estado criado no início do script, fazendo o fstab “sumir” no meio do bootstrap.
Ajuste estrutural do command
Reescrevi o comando com defesa em profundidade:
- criação de
/etc/fstablogo no início; - instalação das dependências críticas para instalador do cPanel;
- configuração SSH/root;
- desativação de serviços conflitantes no contexto do container;
- revalidação de
/etc/fstabantes de entregar para osystemd.
command: >
/bin/bash -c "
# Garante o fstab logo de cara
mkdir -p /etc && touch /etc/fstab &&
# Dependencias essenciais para bootstrap do cPanel
dnf install -y openssh-server passwd systemd iptables-services network-scripts wget perl hostname &&
# SSH + root
echo 'root:${ROOT_PASSWORD}' | chpasswd &&
echo 'Port ${SSH_PORT_CONTAINER}' >> /etc/ssh/sshd_config &&
sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config &&
# Neutraliza servicos que conflitam no container
systemctl stop firewalld.service || true &&
systemctl disable firewalld.service || true &&
systemctl stop NetworkManager || true &&
systemctl disable NetworkManager || true &&
# Re-garante fstab antes do systemd
[ ! -f /etc/fstab ] && touch /etc/fstab;
systemctl enable sshd &&
exec /usr/lib/systemd/systemd"
Esse ajuste eliminou intermitência no bootstrap e deixou o ambiente previsível para instalação.
Passo 3: arquitetura de espelhamento (sem bind mount destrutivo)
Meu alvo era editar em /home/user/dev no host e refletir direto em /usr/local/cpanel no container.
A tentação inicial era bind mount direto:
- /home/user/dev:/usr/local/cpanel
Por que descartei esse caminho
Se a pasta do host estiver vazia ou desalinhada, você sobrepõe o destino da instalação e “esconde” o conteúdo que o instalador precisa gravar. Resultado: setup inconsistente, cópia manual e alto risco de drift.
Solução adotada
Usei volume nomeado para /usr/local/cpanel, permitindo que o Docker gerencie o armazenamento persistente e faça copy-up no primeiro ciclo:
volumes:
- cpanel_core:/usr/local/cpanel
volumes:
cpanel_core:
Com isso, a instalação populou o volume, e eu passei a consumir esse mesmo volume no host via symlink.
Passo 4: descobrir caminho real do volume e criar ponte para o VS Code
Para identificar a origem real no host, usei docker inspect:
docker inspect cpanel-server --format '{{ range .Mounts }}{{ .Source }}:{{ .Destination }}{{ "\n" }}{{ end }}'
Saída relevante:
/var/lib/docker/volumes/projeto_cpanel_core/_data:/usr/local/cpanel
Depois criei a ponte de trabalho:
ln -s /var/lib/docker/volumes/projeto_cpanel_core/_data /home/user/dev
A partir daí, abri /home/user/dev no VS Code e passei a editar direto no storage persistente do container.
Passo 5: validação final de espelhamento e correção de teste
No teste inicial, cometi erro de sintaxe:
touch teste.txt /usr/local/cpanel/
Isso não criou o arquivo onde eu queria. Corrigi usando caminho absoluto:
touch /usr/local/cpanel/teste.txt
No host, o arquivo apareceu instantaneamente em /home/user/dev, confirmando que o espelhamento estava funcional.
Depois executei a instalação do cPanel no container:
docker exec -it cpanel-server bash -c "cd /home && curl -o latest -L https://securedownloads.cpanel.net/latest && sh latest"
Ao final, validei o volume no host:
ls -la /home/user/dev/ | wc -l
71
Ambiente fechado: edição em tempo real via VS Code no host, refletindo no core do cPanel no container.
Boas práticas que ficaram no playbook
- Em erro de
overlay2comoperation not permitted, validar atributos (chattr) e lock de camada antes de insistir emdocker rm -f. - Evitar mount sensível sobre
/etcsem estratégia clara de bootstrap. - Para cPanel em laboratório, preferir volume nomeado no core e não bind mount “cego”.
- Usar
docker inspectpara mapear origem real de volume antes de criar ponte de desenvolvimento. - Testar espelhamento com caminho absoluto dentro do container para evitar falso negativo de validação.
Conclusão
Essa jornada mostrou que o ganho real não vem de “fazer funcionar uma vez”, mas de fechar uma arquitetura de desenvolvimento reproduzível e estável. Com volume nomeado + symlink no host + bootstrap defensivo, consegui um ambiente sólido para desenvolver plugin e ajustar core do cPanel com ciclo curto de feedback.
Projeto relacionado: https://perciocastelo.com.br/projetos/br/docker-cpanel-dev-environment.html Repositório: https://github.com/sr00t3d/docker-cpanel
Este post está licenciado sob CC BY-NC.
Comentários
Participe da discussão abaixo.
Comentários ainda não configurados. Adicione as opções do Cusdis em /assets/json/config/blog-comments-config.json.