0% прочитано

Laravel і права доступу до журналів: чому файл, створений deploy, перестав бути доступним для www-data

У production ICanUp deployment-команди та PHP-FPM працюють від різних системних користувачів. Проблема з Laravel-журналами зʼявилася тоді, коли новий щоденний файл був створений від deploy із правами 0644, а www-data пізніше вже не міг у нього записувати. Розбираю власника, групу, setgid, 0664, umask і чому chmod 777 тут не є правильним рішенням.

17 вересня 2026 р. 10 хв читанняDevOps

Laravel може працювати без проблем увесь день, а після зміни дати раптом отримати Permission denied під час запису в storage/logs.

У production ICanUp причина виявилася не в самому logging-механізмі Laravel. Причина була на рівні Linux: один і той самий каталог використовували процеси від різних системних користувачів.

Deployment і CLI-команди могли працювати від deploy, а HTTP-запити через PHP-FPM виконувалися від www-data. Якщо саме deploy першим створював новий щоденний файл журналу з правами 0644, www-data пізніше вже не мав права дописувати в нього.

Це не проблема, яку варто виправляти chmod 777. Потрібно зробити так, щоб усі процеси, які мають законне право писати у Laravel logs, отримували сумісного власника, групу та права доступу.
Laravel production log permission flow with deploy creating a daily log and www-data receiving Permission denied because the file is mode 0644
Один каталог, два системні користувачі. Проблема виникає тоді, коли новий файл успадковує правильну групу, але не дає цій групі права на запис.

Симптом виглядав як проблема Laravel

На рівні застосунку проблема проявляється дуже просто: Laravel намагається дописати повідомлення у щоденний файл і не може відкрити його в режимі запису.

Типовий симптом
storage/logs/laravel-YYYY-MM-DD.log Permission deniedCould not be opened in append mode

Якщо дивитися лише на Laravel exception, легко почати перевіряти Monolog, конфігурацію каналу або сам код logging.

Але перша корисна перевірка тут значно простіша: хто є власником файла і які в нього права?

Перша перевірка - власник, група і режим доступу

BASH - Перевірити права Laravel logs
cd /var/www/icanup stat -c '%A %a %U:%G %n' \  storage/logs \  storage/logs/laravel-*.log

Проблемний файл концептуально виглядав так:

Проблемний файл
-rw-r--r-- 644 deploy:www-data storage/logs/laravel-YYYY-MM-DD.log

На перший погляд група правильна: www-data.

Але 0644 означає, що записувати може лише власник файла. Група має тільки читання.

Чому deploy може писати, а www-data вже ні

Процес

Системний користувач

Що він робить

Deployment та Artisan CLI

deploy

Оновлює застосунок, виконує Artisan-команди і теж може створювати log-файли

PHP-FPM

www-data

Обробляє HTTP-запити і записує application logs

Queue worker

Залежить від production-конфігурації

Також може записувати в ті самі журнали

Проблема не в тому, що один із цих користувачів "неправильний". Обидва мають нормальну роль.

Проблема виникає, коли спільний ресурс не має чітко визначеного контракту прав доступу.

0644 пояснює проблему майже повністю

Розряд

Права

Власник

rw- - читання і запис

Група

r-- - тільки читання

Інші

r-- - тільки читання

Для файла deploy:www-data із правами 0644 користувач deploy може писати.

PHP-FPM від www-data потрапляє в права групи, а там запису немає.

Саме тому правильна група без права w проблему не вирішує.

Чому проблема могла зʼявлятися тільки після створення нового daily log

Laravel daily channel створює новий файл для нового дня.

Старий файл міг мати правильні права після ручного виправлення. Але наступного дня першим до logging міг звернутися deployment або Artisan-процес від deploy, і новий файл знову створювався з іншим режимом доступу.

Тому одноразово виправити вже існуючий файл недостатньо.

Якщо проблема повертається після ротації або створення нового log-файла, потрібно виправляти правило створення майбутніх файлів, а не лише поточний файл.

chmod 777 прибирає симптом, але руйнує модель доступу

Найшвидша спокуса в такій ситуації:

BASH - Так робити не потрібно
chmod -R 777 storage

Після цього проблема справді може зникнути, тому що запис дозволений усім.

Але разом із проблемою зникає і сама межа доступу.

  • Будь-який локальний користувач або process із доступом до системи отримує зайві права.
  • Стає складніше зрозуміти, хто насправді повинен мати право на запис.
  • Наступна permission-проблема маскується замість того, щоб бути виправленою.
  • Production-конфігурація починає залежати від надмірно широких прав.

Правильне рішення для спільного каталогу - не дозволити запис усім, а визначити конкретну групу процесів, які мають право записувати.

Спочатку я вирівнюю права вже існуючих logs

BASH - Виправити власника і права існуючих журналів
cd /var/www/icanup sudo chown -R deploy:www-data storage/logs sudo find storage/logs \  -type d \  -exec chmod 2775 {} \; sudo find storage/logs \  -type f \  -exec chmod 0664 {} \;

Для файлів результатом є 0664:

Очікувані права файла
-rw-rw-r-- deploy:www-data laravel-YYYY-MM-DD.log

Тепер і власник deploy, і члени групи www-data мають право на запис.

Але ключова частина рішення - setgid на каталозі

Права 2775 на каталозі містять setgid bit.

Для спільного каталогу це важливо: нові файли та підкаталоги успадковують групу каталогу замість випадкової primary group процесу, який їх створив.

BASH - Закріпити спільну групу через setgid
sudo chown deploy:www-data storage/logssudo chmod 2775 storage/logs stat -c '%A %a %U:%G %n' storage/logs
Каталог із setgid
drwxrwsr-x 2775 deploy:www-data storage/logs

Літера s у групових правах означає, що setgid активний.

Це вирішує питання успадкування групи, але ще не гарантує групі право на запис у кожен новий файл.

Новий файл теж повинен створюватися з 0664

Для Laravel daily channel я хочу чітко визначити permission для нових log-файлів.

У конфігурації logging це можна зробити на рівні самого каналу.

PHP - Задати права для нових щоденних журналів
'daily' => [    'driver' => 'daily',    'path' => storage_path('logs/laravel.log'),    'level' => env('LOG_LEVEL', 'debug'),    'days' => 14,    'permission' => 0664,    'locking' => true,],

Точний склад channel configuration залежить від проєкту. Для цієї проблеми важливий сам контракт: новий файл не повинен випадково ставати доступним для запису лише тому користувачу, який створив його першим.

umask доповнює цей контракт

Окрім Laravel configuration, важливо враховувати umask процесів, які створюють спільні файли.

Для deployment shell значення 0002 дозволяє новим group-shared файлам залишати право запису для групи.

BASH - Сумісний umask для спільних файлів
umask 0002

Це особливо важливо, якщо deployment створює не тільки Laravel logs, а й інші файли, які пізніше повинні використовуватися процесами тієї самої групи.

Я не використовую umask 0000. Мені не потрібно давати право на запис усім.

Я перевіряю права від обох користувачів

Після зміни прав недостатньо подивитися на ls -l. Я хочу перевірити доступ так, як його бачать реальні production-процеси.

BASH - Перевірити каталог від deploy і www-data
cd /var/www/icanup sudo -u deploy \  test -w storage/logs \  && echo 'deploy can write' sudo -u www-data \  test -w storage/logs \  && echo 'www-data can write'

Якщо щоденний файл уже існує, перевіряю і його окремо:

BASH - Перевірити запис у поточний щоденний журнал
LOG_FILE="$(ls -1t storage/logs/laravel-*.log | head -1)" sudo -u deploy \  test -w "$LOG_FILE" \  && echo 'deploy can write the log' sudo -u www-data \  test -w "$LOG_FILE" \  && echo 'www-data can write the log'

Корисно також перевірити весь шлях до файла

Навіть правильний файл не допоможе, якщо один із батьківських каталогів не дає процесу пройти до нього.

BASH - Перевірити права кожного каталогу в шляху
namei -l \  /var/www/icanup/storage/logs/laravel-YYYY-MM-DD.log

Після виправлення я перевіряю саме наступний новий файл

Це найважливіша частина перевірки.

Якщо я виправила лише поточний файл, проблема може повернутися наступного дня. Тому правильний результат підтверджується лише тоді, коли новий daily log автоматично отримує правильну групу і режим 0664.

Що перевірити

Очікуваний результат

Власник

Може залежати від того, який process створив файл

Група

www-data або інша визначена спільна група

Права

0664

Каталог

setgid активний

PHP-FPM

Може дописати у файл

Deployment / CLI

Також може дописати у файл

Це частина production-контракту, а не одноразова серверна команда

Після цього випадку я перестала сприймати права на storage як разове налаштування під час першого deployment.

Це частина production-контракту між кількома процесами.

  • deploy має чітко визначені права.
  • www-data має чітко визначені права.
  • Спільні writable-каталоги мають спільну групу.
  • Каталоги використовують setgid там, де потрібне успадкування групи.
  • Нові файли створюються з group-write permission.
  • Deployment не виправляє проблему через 777.
  • Перевірка охоплює не лише поточні, а й майбутні файли.

Це та сама причина, через яку я намагаюся робити production deployment відтворюваним, а не набором ручних виправлень на сервері. Загальніший процес я описувала у матеріалі про реальний Laravel CI/CD pipeline.

Що я не роблю

  • Не використовую chmod -R 777 storage як постійне рішення.
  • Не виправляю лише один поточний log-файл і не вважаю задачу завершеною.
  • Не змінюю PHP-FPM на root заради обходу permission problem.
  • Не запускаю application-команди через sudo без розуміння, які файли вони створять.
  • Не покладаюся лише на правильну групу без group-write permission.
  • Не покладаюся лише на 0664, якщо майбутні файли можуть успадкувати неправильну групу.
  • Не перевіряю доступ тільки від свого SSH-користувача.

Мій короткий checklist для Laravel logs у production

  • Визначити всіх системних користувачів, які можуть записувати Laravel logs.
  • Перевірити owner, group і mode через stat.
  • Перевірити PHP-FPM user.
  • Використати спільну групу для процесів, яким справді потрібен запис.
  • Встановити setgid на shared writable directory.
  • Для log-файлів використовувати 0664, якщо групі потрібен запис.
  • Перевірити Laravel daily channel permission.
  • Перевірити сумісний umask процесів, які створюють спільні файли.
  • Перевірити каталог і файл від deploy та www-data окремо.
  • Повторити перевірку після створення нового daily log.

Permission problem вважаю виправленою не тоді, коли поточний файл знову writable, а тоді, коли наступний файл автоматично створюється правильно.

Висновок

Проблема Laravel logs у production ICanUp виявилася типовим конфліктом між кількома системними користувачами.

deploy міг першим створити новий щоденний log. Файл отримував групу www-data, але режим 0644 давав цій групі лише читання. Коли PHP-FPM від www-data намагався записати наступне повідомлення, він отримував Permission denied.

Одноразовий chmod лікує лише поточний файл. 777 просто прибирає межі доступу.

Стабільне рішення складається з кількох частин: спільна група, setgid для каталогу, 0664 для log-файлів, сумісний umask і перевірка доступу від кожного реального production-користувача.

Головний урок для мене: права доступу до shared runtime files потрібно проєктувати так само явно, як database credentials, queue workers або deployment flow.