Під час локальної роботи з Laravel і Vite у ICanUp я отримала помилку, яка спочатку виглядала як проблема самого Vite.
Dev server намагався оновити свій кеш у node_modules/.vite, але не міг видалити один із файлів.
Причина виявилася простішою: файл був створений іншим системним користувачем і поточний користувач не мав права його змінювати.
Error: EACCES: permission denied, unlink'node_modules/.vite/deps/package.json'
Спочатку я перевірила не Vite, а власника файлів
Коли в повідомленні є EACCES, найкорисніше спочатку подивитися на файлову систему.
Не потрібно одразу перевстановлювати весь frontend або міняти Vite config.
ls -ld \ node_modules \ node_modules/.vite \ node_modules/.vite/deps stat -c '%U:%G %a %n' \ node_modules \ node_modules/.vite \ node_modules/.vite/deps \ node_modules/.vite/deps/package.jsonЯкщо проєкт належить моєму звичайному користувачу, а всередині node_modules/.vite раптом з'являється root:root, причина практично вже знайдена.
Чому root-owned файл взагалі з'являється в node_modules
Найчастіше такий стан виникає не під час звичайного npm run dev, а після змішування різних способів запуску Node.
Наприклад, один раз Vite запустили через контейнер від root або через sudo, а наступного разу - від звичайного користувача на host.
- Vite або npm запускали через
sudo. - Docker container працював від
rootі писав у bind-mounted project directory. - Залежності встановлювалися одним користувачем, а dev server запускався іншим.
- Host Node і container Node по черзі використовували той самий
node_modulesбез узгодженого UID/GID.
First run: root | vnode_modules/.vite/deps/package.jsonowner = root Next run: alyona | vVite tries to unlink package.json | vEACCESЩо саме Vite зберігає в node_modules/.vite
Каталог node_modules/.vite не є моїм application source code.
Vite використовує його для згенерованого кешу і попередньо оброблених залежностей, щоб наступні запуски dev server були швидшими.
Тому видалення .vite не видаляє код застосунку або встановлені package definitions. Vite може створити цей кеш знову.
Шлях | Роль | Можна перегенерувати |
|---|---|---|
| Код застосунку | Ні |
| Опис залежностей і scripts | Ні |
| Зафіксовані версії залежностей | Ні |
| Згенерований кеш Vite | Так |
Якщо проблема тільки в .vite, не потрібно чіпати весь node_modules
Я починаю з найменшого можливого виправлення.
Якщо node_modules загалом належить правильному користувачу, а проблема обмежена Vite cache, достатньо прибрати цей кеш і дати Vite створити його заново.
rm -rf node_modules/.vite npm run devsudo rm -rf node_modules/.vite npm run devТут є важлива різниця: sudo використовується лише для видалення вже створеного root-owned кешу.
Новий npm run dev я запускаю без sudo.
Коли видалення .vite вже недостатньо
Іноді проблема не обмежується одним кешем.
Якщо npm install або інший Node process раніше працював від root, root-owned файли можуть бути розкидані по всьому node_modules.
find node_modules \ -user root \ -print \ | head -n 50Якщо команда показує багато результатів, локальне видалення .vite усуває лише поточний симптом.
Тоді потрібно відновити нормальний ownership для всього дерева залежностей.
sudo chown -R \ "$(id -un)":"$(id -gn)" \ node_modulesІнший чистий варіант - перевстановити dependencies правильним користувачем
Якщо ownership усього node_modules хаотичний або я не довіряю його поточному стану, інколи простіше створити залежності заново.
sudo rm -rf node_modules npm cisudo тут знову потрібен тільки для видалення root-owned дерева. Сам npm ci запускається вже від звичайного користувача.
Цей підхід доречний, коли в проєкті є актуальний package-lock.json.
Чому chmod 777 не є виправленням
Побачивши permission error, легко спробувати зробити каталог writable для всіх.
Це приховує причину, але не виправляє модель ownership.
# Do not use this as a fix:chmod -R 777 node_modulesПравильне питання не "як дозволити всім писати?", а "який користувач повинен володіти файлами і чому їх створив root?".
Чому sudo npm run dev тільки погіршує проблему
Запуск sudo npm run dev може тимчасово прибрати EACCES, тому що root має право змінити root-owned cache.
Але після цього нові файли Vite знову створюються від root.
Наступний запуск без sudo повертає ту саму проблему.
Wrong recovery: EACCES | vsudo npm run dev | vmore root-owned files | vnormal npm run dev | vEACCES againDocker ускладнює ownership через UID і GID
У Docker ім'я користувача всередині контейнера і на host може відрізнятися. Для bind mount важливими є UID і GID процесу, який реально створює файл.
Якщо container process має UID 0, файл на host може стати власністю root.
id -uid -g docker compose exec workspace idНазва service у конкретному Laradock setup може бути іншою, тому я перевіряю той service, у якому фактично запускаю Node.
У Laradock я хочу один стабільний ownership contract
Найкраще рішення - не виправляти permissions після кожного запуску, а зробити так, щоб процеси від початку створювали файли правильним користувачем.
Для bind-mounted project directory container user повинен бути узгоджений з host UID/GID або використовувати іншу чітко визначену модель.
- Не запускати Node commands від root без необхідності.
- Перевірити UID/GID користувача у Node-capable container.
- Не змішувати root container і host user для одного
node_modules. - Якщо Node запускається на host - послідовно використовувати host user.
- Якщо Node запускається в container - послідовно використовувати налаштованого non-root user.
- Після зміни Laradock configuration один раз очистити старі root-owned artifacts.
Multi-PHP setup не означає, що Node теж треба запускати різними способами
У мене локально кілька PHP runtime для різних проєктів, і це зручно вирішується окремими Laradock workspace та wrapper-командами.
Але Node filesystem ownership - інша задача.
Навіть якщо PHP 8.3, 8.4 і 8.5 живуть у різних services, файли frontend одного проєкту все одно повинні мати один зрозумілий ownership contract.
Про сам multi-PHP workflow я окремо писала у статті про PHP 8.3, 8.4 і 8.5 у Laradock з dphp, dartisan та dcomposer.
PHP upgrade і Node permissions можуть перетнутися випадково
Під час оновлення runtime легко виконати частину команд у container, частину на host, а окрему команду випадково запустити від root.
Після цього проблема проявляється вже пізніше, коли Vite намагається перегенерувати кеш.
У мене вже був окремий великий upgrade Laravel environment до PHP 8.5, який я описувала у матеріалі про PHP 8.5, Laravel, Laradock і Composer package upgrade. Такі зміни особливо добре показують, чому runtime users потрібно контролювати окремо від версій PHP.
Мій порядок виправлення EACCES
Перевірка | Дія |
|---|---|
Root-owned тільки | Видалити |
Root-owned багато файлів у | Виправити ownership або перевстановити dependencies |
Root-owned весь project | Знайти процес, який створює файли від root, а не робити blind chmod |
Проблема повертається після Docker | Перевірити UID/GID container process |
Як я перевіряю, що проблема справді виправлена
find node_modules \ -user root \ -print \ -quit npm run dev- Vite запускається без
sudo. node_modules/.viteстворюється поточним користувачем.- Повторний запуск dev server не дає
EACCES. - У
node_modulesне з'являються нові root-owned файли. - Docker або Laradock не повертає проблему після наступного запуску.
package.jsonіpackage-lock.jsonне довелося змінювати для permission fix.
Що я б не робила
- Не запускала б
sudo npm run dev. - Не запускала б
sudo npm installяк звичайний спосіб роботи. - Не використовувала б
chmod -R 777 node_modules. - Не видаляла б весь
node_modules, якщо проблема тільки у Vite cache. - Не робила б recursive
chownусього project directory без перевірки. - Не змішувала б host Node і root container Node для одного dependency tree.
- Не шукала б проблему в Laravel application code, поки filesystem уже показує root ownership.
EACCES у Vite часто виглядає як frontend problem, але насправді це простий конфлікт між користувачем, який створив кеш, і користувачем, який намагається його оновити.
Що я залишила собі на майбутнє
- При EACCES спочатку перевіряти owner і permissions.
node_modules/.viteвважати generated cache, який можна перегенерувати.- Починати з найменшого можливого виправлення.
- Використовувати sudo тільки для очищення вже неправильних root-owned artifacts, а не для запуску Node.
- Не замінювати ownership проблему широкими permissions.
- У Docker перевіряти UID/GID процесу, який пише в bind mount.
- У Laradock зберігати один стабільний ownership contract для frontend files.
- Після runtime upgrade перевіряти, від якого користувача виконувались Node commands.
Висновок
Помилка EACCES: permission denied, unlink у моєму випадку не вимагала змін у Laravel або Vite configuration.
Vite просто намагався оновити свій generated cache, але частина node_modules/.vite належала root.
У ICanUp правильним рішенням стало відновити ownership contract: видалити або виправити root-owned artifacts і надалі запускати Node одним узгодженим non-root користувачем.
Найважливіше тут не навчити Vite обходити permissions, а перестати створювати frontend files неправильним користувачем.



