Docs›Começando›Deploy no cPanel
Começando

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, roda migrate --force + db:seed --class=DatabaseSeeder, cria o admin e sela a instalação (storage/mad-installed.lock). Em hospedagem compartilhada, defina INSTALL_TOKEN no .env antes de abrir /install — assim você não precisa de SSH nem pra ler storage/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 usa database/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 sob public_html/public/. Em shared hosting com tráfego real, prefira MySQL/MariaDB via DB_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=55 nunca 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ão iam, não na conexão default do Laravel — config/queue.php usa env('DB_QUEUE_CONNECTION', 'iam'). Repontar DB_CONNECTION sem repontar DB_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 + cd antes do comando)
  • Pastas storage/, bootstrap/cache/ e tmp/ 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