Deploy no cPanel
Configuração para hospedagem compartilhada.
Guia de configuração dos serviços em ambientes cPanel — shared hosting e VPS.
O MAD é um app Laravel comum: o entry point é public/index.php e todo
comando administrativo passa por php artisan, não por um binário separado.
1. Instalação inicial
Após fazer o upload dos arquivos via FTP/Git:
# No terminal SSH do cPanel
cd /home/SEU_USER/public_html
# Instalar dependências PHP
composer install --no-dev --optimize-autoloader
# Se o projeto usa o shell padrão (Vite + Tailwind), compilar os assets
npm ci
npm run build
# .env com as credenciais de produção (DB_*, APP_KEY, APP_URL...) — ver
# a página de Configuração. Gerar a chave se ainda não existir:
php artisan key:generate
# Rodar as migrations (cobre as conexões lógicas do MAD — iam, log,
# business, comm, ged, ai — numa passada só)
php artisan migrate --force
# Verificar status
php artisan migrate:status
# Cachear config/rotas/views em produção (recompile após qualquer deploy)
php artisan config:cache
php artisan route:cache
php artisan view:cache
Primeira vez subindo o projeto? O instalador web (
/install) faz esse setup (banco + admin) por um wizard guiado em vez de SSH manual — ver Instalador web (/install). Ele grava o.env, rodamigrate --force+db:seed --class=DatabaseSeeder, cria o admin e sela a instalação (storage/mad-installed.lock). Em hospedagem compartilhada, definaINSTALL_TOKENno.envantes de abrir/install— assim você não precisa de SSH nem pra lerstorage/app/install-token.txt.
SQLite em produção? As 6 conexões MAD apontam por padrão para
app/database/mad.sqlite(a conexão default do Laravel usadatabase/database.sqlite). Os dois arquivos — e os diretórios que os contêm — precisam ser graváveis pelo usuário do PHP, e nenhum dos dois pode ficar sobpublic_html/public/. Em shared hosting com tráfego real, prefira MySQL/MariaDB viaDB_MAD_DRIVER— ver Configuração.
2. Descobrindo o path do PHP
No terminal SSH:
which php
# /usr/local/bin/php
php -v
# PHP 8.3.x ...
Ou via PHP:
php -r "echo PHP_BINARY;"
No cPanel o path mais comum é
/usr/local/bin/php. Alguns servidores têm múltiplas versões — use o seletor de PHP do cPanel para escolher 8.3+ (requisito do framework) antes de verificar.
3. Cron Jobs no cPanel
Acesse cPanel → Cron Jobs → Add New Cron Job.
Scheduler (obrigatório)
Configura o agendamento de tarefas. Um único cron entry gerencia todas as tarefas registradas em routes/console.php (Schedule::command(...)).
Minute: *
Hour: *
Day: *
Month: *
Weekday: *
Command:
cd /home/SEU_USER/public_html && /usr/local/bin/php artisan schedule:run >> /home/SEU_USER/logs/scheduler.log 2>&1
Queue Worker (shared hosting)
Em shared hosting, use --max-time=55 para que o worker rode dentro de um ciclo de 1 minuto e encerre graciosamente:
Minute: *
Hour: *
Day: *
Month: *
Weekday: *
Command:
cd /home/SEU_USER/public_html && /usr/local/bin/php artisan queue:work --max-time=55 >> /home/SEU_USER/logs/queue.log 2>&1
O
--max-time=55nunca interrompe um job no meio da execução. O worker aguarda o job atual terminar e só então encerra.
Múltiplas filas
Se tiver filas separadas (emails, relatórios, etc.):
# Fila padrão
cd /home/SEU_USER/public_html && /usr/local/bin/php artisan queue:work --queue=default --max-time=55 >> /home/SEU_USER/logs/queue-default.log 2>&1
# Fila de emails (ver página "Mail" — MailService enfileira 2FA, reset de senha, convites)
cd /home/SEU_USER/public_html && /usr/local/bin/php artisan queue:work --queue=emails --max-time=55 >> /home/SEU_USER/logs/queue-emails.log 2>&1
4. Logs
Crie a pasta de logs antes de configurar os crons:
mkdir -p /home/SEU_USER/logs
Para visualizar os logs em tempo real:
tail -f /home/SEU_USER/logs/scheduler.log
tail -f /home/SEU_USER/logs/queue.log
O Laravel também grava em storage/logs/laravel.log (canal LOG_CHANNEL do .env) — vale conferir os dois.
Para limitar o tamanho dos logs (adicionar rotação):
# Adicionar cron de limpeza semanal
0 0 * * 0 truncate -s 0 /home/SEU_USER/logs/scheduler.log
0 0 * * 0 truncate -s 0 /home/SEU_USER/logs/queue.log
5. Permissões de arquivo
# storage/ e bootstrap/cache/ precisam ser graváveis pelo PHP (sessões,
# cache de view/config/rota, uploads em progresso, spool do MadTrace)
chmod -R 775 storage bootstrap/cache
# Garante que tmp/ (uploads em progresso de campos de arquivo) é gravável
chmod -R 775 tmp
Ajuste o owner/grupo para o usuário do servidor web do seu plano cPanel. 775 com owner correto é mais seguro que 777 — use 777 só como último recurso em dev local.
6. Produção com VPS (cPanel + WHM)
Em VPS onde você tem acesso root, use Supervisor para rodar o worker como daemon:
Instalar Supervisor
# CentOS/AlmaLinux (mais comum em cPanel)
yum install -y supervisor
# Ubuntu/Debian
apt install -y supervisor
systemctl enable supervisord
systemctl start supervisord
Configurar o worker
Criar /etc/supervisor/conf.d/mad-worker.conf:
[program:mad-worker-default]
command=/usr/local/bin/php /home/SEU_USER/public_html/artisan queue:work --queue=default --sleep=3 --tries=3
directory=/home/SEU_USER/public_html
user=SEU_USER
autostart=true
autorestart=true
numprocs=1
redirect_stderr=true
stdout_logfile=/home/SEU_USER/logs/worker-default.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=3
[program:mad-worker-emails]
command=/usr/local/bin/php /home/SEU_USER/public_html/artisan queue:work --queue=emails --sleep=3 --tries=3
directory=/home/SEU_USER/public_html
user=SEU_USER
autostart=true
autorestart=true
numprocs=1
redirect_stderr=true
stdout_logfile=/home/SEU_USER/logs/worker-emails.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=3
# Aplicar configuração
supervisorctl reread
supervisorctl update
# Verificar status
supervisorctl status
# Reiniciar worker após deploy
supervisorctl restart mad-worker-default
supervisorctl restart mad-worker-emails
Cron do scheduler no VPS
# No crontab do usuário (crontab -e)
* * * * * cd /home/SEU_USER/public_html && /usr/local/bin/php artisan schedule:run >> /home/SEU_USER/logs/scheduler.log 2>&1
7. Checklist de deploy
[ ] composer install --no-dev --optimize-autoloader
[ ] npm ci && npm run build (se o projeto usa o shell Vite/Tailwind)
[ ] .env configurado (DB_*, APP_KEY, APP_URL, MAIL_*, QUEUE_CONNECTION...)
[ ] php artisan migrate --force (todas as conexões MAD numa passada)
[ ] php artisan migrate:status (verificar que tudo rodou)
[ ] php artisan config:cache / route:cache / view:cache
[ ] chmod -R 775 storage bootstrap/cache tmp
[ ] mkdir -p logs (ou /home/user/logs)
[ ] Cron do scheduler configurado (* * * * * → php artisan schedule:run)
[ ] Cron/Supervisor do worker configurado (php artisan queue:work)
[ ] php artisan queue:failed (verificar filas/falhas)
[ ] php artisan schedule:list (verificar tarefas agendadas)
[ ] /install fechado — storage/mad-installed.lock existe (GET /install → 302
pro login; POST /install/_wire → 403). Se instalou por SSH sem o wizard,
o InstallGuard auto-sela no primeiro acesso ao detectar o usuario admin.
As tabelas de fila (
jobs/failed_jobs) vivem na conexãoiam, não na conexão default do Laravel —config/queue.phpusaenv('DB_QUEUE_CONNECTION', 'iam'). RepontarDB_CONNECTIONsem repontarDB_IAM_*deixa o worker olhando pro banco errado.
8. Resumo por tipo de hosting
| Recurso | Shared Hosting | VPS/Dedicado |
|---|---|---|
| Scheduler | ✅ Cron * * * * * |
✅ Cron * * * * * |
| Queue Worker | ✅ Cron com --max-time=55 |
✅ Supervisor (daemon) |
| SSH | Depende do plano | ✅ Sempre |
| Supervisor | ❌ | ✅ |
| Múltiplos workers | ❌ Limitado | ✅ numprocs=N |
9. Problemas comuns
"Command not found: php"
Use o path completo: /usr/local/bin/php ou /usr/bin/php
which php # descobre o path correto
Cron não executa
Verifique:
- Path do PHP correto (8.3+)
- Path do projeto correto (use path absoluto +
cdantes do comando) - Pastas
storage/,bootstrap/cache/etmp/com permissão de escrita - Log de erros no cPanel: cPanel → Cron Jobs → ver output
Worker morre imediatamente
# Rodar manualmente para ver o erro
php artisan queue:work --once -v
Jobs ficam presos como "reserved"
Se o worker morreu enquanto processava, o job fica com reserved_at preenchido. O Illuminate libera automaticamente após o timeout (padrão 60s). Para forçar:
php artisan queue:clear --queue=default
# ou
php artisan queue:retry all