MAATRIX / Блог / Cachet на сервере: частые ошибки и решения

Cachet на сервере: частые ошибки и решения

MAATRIX

Cachet — открытая статус-страница на Laravel: компоненты, инциденты, метрики и подписка по email в одном интерфейсе. Ставится за полчаса, а потом всплывают вещи, которые в документации упомянуты вскользь: белый экран сразу после установки, битые ссылки на CSS за reverse-прокси, метрики, которые не считаются сами, и письма подписчикам, которые уходят в никуда. Разбираем по причинам, а не по симптомам — большинство проблем Cachet сводится к четырём вещам: правам на файлы, окружению за прокси, cron и очереди.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Установка: где ломается чаще всего

Cachet — приложение на Laravel, и первые ошибки — это ошибки Laravel, а не самого Cachet. Три места, где спотыкаются почти все:

Память при composer install. На VPS с 512 МБ – 1 ГБ ОЗУ composer съедает больше, чем кажется, особенно при разрешении зависимостей без --no-dev. Типичный обрыв — процесс просто убит, в логе ничего внятного. Проверка:

dmesg -T | grep -i 'killed process'
free -h

Если видите Out of memory: Killed process ... (php) — временный своп решает проблему на время установки:

fallocate -l 1G /swapfile && chmod 600 /swapfile
mkswap /swapfile && swapon /swapfile
composer install --no-dev --optimize-autoloader

No application encryption key has been specified. Классика Laravel: .env скопирован из .env.example, а APP_KEY не сгенерирован. Одна команда:

cp .env.example .env
php artisan key:generate

Без неё зашифрованные сессии и cookie не создаются, и вход в админку падает с ошибкой 500 ещё до формы логина.

Кодировка базы. Cachet рассчитан на utf8mb4. Если база или таблицы созданы с utf8 (старый дефолт у части MySQL-инсталляций), миграции падают на индексах с ошибкой вида Specified key was too long. Создавайте базу заранее с явной кодировкой:

CREATE DATABASE cachet CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

и только после этого php artisan migrate --seed.

Белый экран и 500 сразу после установки: права на storage

Самая частая жалоба новичков — сайт открывается, но вместо статус-страницы белый экран или 500 Server Error без деталей. Причина в 95% случаев одна: Laravel не может писать в storage/ и bootstrap/cache/ от имени пользователя веб-сервера.

chown -R www-data:www-data storage bootstrap/cache
find storage bootstrap/cache -type d -exec chmod 775 {} \;
find storage bootstrap/cache -type f -exec chmod 664 {} \;

Подставьте своего пользователя веб-сервера, если у вас не www-data (на некоторых сборках nginx+php-fpm это nginx или отдельный пул). Прежде чем чинить вслепую, включите подробный вывод и посмотрите настоящую причину — она всегда лежит в storage/logs/laravel.log:

tail -n 50 storage/logs/laravel.log

Там же находятся и другие частые причины 500: не хватает PHP-расширения (ext-bcmath, ext-gd, ext-mbstring), не создана папка storage/framework/{sessions,views,cache} (Cachet не создаёт их сам при первом клоне из git — только composer-пакет это делает при установке через архив), или .env содержит опечатку в DB_*.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Статус-страница за reverse-прокси: APP_URL и битые ссылки

Если Cachet стоит за nginx или Traefik с включённым HTTPS (а в 2026 году иначе публичную статус-страницу и не держат), появляется вторая типовая беда: страница открывается, но без стилей и с ссылками на http:// вместо https://, либо форма логина уводит на редирект-петлю.

Причина — APP_URL в .env не совпадает с тем, как страницу видит браузер:

APP_URL=https://status.example.com

Значение должно быть ровно тем адресом, по которому открывается сайт — без завершающего слэша, с правильной схемой. Laravel использует его для генерации ссылок на ассеты и абсолютных URL в письмах-уведомлениях.

Второй кусок той же проблемы — приложение за прокси не знает, что внешнее соединение было по HTTPS, и Laravel генерирует http:// ссылки, даже если APP_URL указан верно. Нужно явно доверять прокси в app/Http/Middleware/TrustProxies.php (или через TRUSTED_PROXIES в новых версиях):

protected $proxies = '*';
protected $headers = Request::HEADER_X_FORWARDED_ALL;

и убедиться, что nginx перед Cachet действительно передаёт заголовки:

proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;

Если сертификат сам по себе не выпускается или nginx ругается на конфигурацию SSL — это отдельная, более базовая проблема, разобрана в статье про ошибки nginx с SSL.

Без cron метрики и отчёты не считаются сами

Это то, что чаще всего сбивает с толку после успешной установки: инциденты создаются вручную нормально, но графики метрик (Response Time и любые кастомные) остаются пустыми, а «Report is generated automatically» так и не появляется.

Cachet, как и любое Laravel-приложение, не запускает фоновые задачи сам — за это отвечает системный cron, который раз в минуту дёргает планировщик Laravel:

crontab -e -u www-data
* * * * * cd /var/www/cachet && php artisan schedule:run >> /dev/null 2>&1

Именно этой строкой пересчитываются точки метрик, закрываются просроченные maintenance-окна и формируются агрегированные отчёты. Без неё Cachet выглядит рабочим, но фактически не эволюционирует — компоненты и инциденты остаются статичными до ручного вмешательства. Проверить, что планировщик реально срабатывает, можно принудительным прогоном и логом:

php artisan schedule:run -v
grep CRON /var/log/syslog | tail -20

Если по общим принципам настройки cron на сервере есть сомнения — например, задача не срабатывает вовсе или срабатывает не тем пользователем — стоит свериться со статьёй про типовые ошибки cron-задач.

Подписчики не получают email-уведомления

Подписка на инциденты по email — одна из основных причин ставить статус-страницу вообще, и одна из самых недоделанных частей у большинства инсталляций. Симптом: подписка оформляется без ошибок, письмо с подтверждением не приходит либо приходит с задержкой в часы.

Здесь две независимые причины, и путают их постоянно.

Не настроен транспорт. В .env должны быть реальные параметры SMTP, а не дефолтные заглушки:

MAIL_MAILER=smtp
MAIL_HOST=smtp.example.com
MAIL_PORT=587
MAIL_USERNAME=status@example.com
MAIL_PASSWORD=секрет
MAIL_ENCRYPTION=tls

Проверка без похода в браузер:

php artisan tinker
>>> Mail::raw('test', function($m){ $m->to('you@example.com')->subject('test'); });

Если многие VPS-провайдеры по умолчанию блокируют исходящий 25-й порт (это защита от спам-ботов, а не баг), для собственной отправки писем нужен либо открытый провайдером 587/465, либо внешний SMTP-релей — так надёжнее в любом случае, потому что репутация IP у постоянно арендуемого сервера обычно ниже, чем у специализированного почтового сервиса.

Очередь стоит, а обработчика нет. Если в .env указано QUEUE_CONNECTION=database (а не sync), письма не отправляются сразу — они складываются в таблицу jobs и ждут воркер. Без запущенного queue:work они там и останутся, накапливаясь бесконечно:

php artisan queue:work --tries=3 --sleep=3

В проде это держат постоянным процессом через supervisor, а не в интерактивной сессии:

[program:cachet-queue]
command=php /var/www/cachet/artisan queue:work --tries=3 --sleep=3
directory=/var/www/cachet
user=www-data
autostart=true
autorestart=true
numprocs=1

Быстрая диагностика — просто посчитать необработанные задачи:

mysql -e "SELECT COUNT(*) FROM cachet.jobs;"

Число растёт, а не падает до нуля — воркер не работает.

Резервные копии, апдейты и на что рассчитывать от проекта

Всё состояние Cachet — это база данных плюс .env с ключами и настройками почты. Бэкапить нужно именно их, а не файлы приложения (они восстанавливаются из git/архива за минуту):

mysqldump -u root -p --single-transaction cachet > cachet_$(date +%F).sql

Перед любым php artisan migrate на уже работающей инсталляции — обязательный дамп базы: миграции Laravel в общем случае необратимы без отдельного down()-метода, и часть из них в реальных апдейтах его не реализует аккуратно. Если общая методика бэкапа MySQL/MariaDB на сервере ещё не отлажена, стоит посмотреть статью про частые ошибки бэкапа MySQL — там разобраны типовые грабли вроде блокировок при --single-transaction на InnoDB и ошибок доступа.

Отдельная вещь, которую стоит учитывать честно: активность апстрима Cachet последние годы невысокая, релизы выходят редко, а совместимость с новыми версиями PHP проверяется небыстро. Это не значит, что проект нельзя использовать в проде — многие статус-страницы годами крутятся на стабильной версии без проблем, — но означает, что не стоит гнаться за последним PHP на сервере ради галочки: зафиксируйте рабочую связку PHP + Cachet, протестируйте апдейт на отдельном staging-сервере и только потом катите в прод. Если задача шире, чем публичная статус-страница, и нужен ещё и мониторинг доступности сервисов с алертами, для этого обычно ставят отдельный инструмент — например, Uptime Kuma, который решает другую задачу и хорошо сочетается с Cachet как источник данных для инцидентов.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Cachet работает без cron вообще?

Формально сайт открывается и инциденты можно вести руками, но метрики, автоматические отчёты и закрытие плановых работ по расписанию требуют schedule:run в cron — без него часть функциональности просто не активна.

Почему после установки за Cloudflare или другим прокси форма логина уходит в бесконечный редирект?

Обычно рассинхрон между APP_URL (или его схемой) и TrustProxies — Laravel считает, что соединение идёт по HTTP, и генерирует ссылки не той схемы, из-за чего браузер и сервер спорят о протоколе.

Сколько ресурсов реально нужно под Cachet?

Само приложение лёгкое — типичная связка nginx + php-fpm + MySQL спокойно работает на 1 vCPU / 1 ГБ ОЗУ для десятков компонентов и умеренной аудитории подписчиков; узкое место обычно не CPU, а память при composer install на этапе установки/обновления.

Можно ли держать очередь в sync и не запускать queue:work?

Можно — тогда письма отправляются синхронно при каждом действии, без отдельного воркера, но при недоступности SMTP это будет тормозить или ронять запрос пользователя, который создал инцидент. Для прода с реальным трафиком выгоднее database/redis-очередь и отдельный воркер под supervisor.

Что делать, если после апдейта миграция упала на середине?

Восстановить базу из дампа, снятого перед апдейтом (см. выше), разобрать конкретную ошибку миграции в логе и накатывать по шагам на отдельном staging, а не в проде — откатить одну миграцию «назад» в Cachet штатно не всегда возможно.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →