0% прочитано

Vite EACCES in Laravel: Why node_modules/.vite Becomes Root-Owned and How to Fix It

While working locally with Laravel and Vite, I hit EACCES: permission denied for node_modules/.vite/deps/package.json. The problem was not Vite itself but filesystem ownership: part of the cache had previously been created by root through Docker or a sudo command. I explain how to inspect ownership, when deleting .vite is enough, when the whole node_modules tree needs repair, and how to prevent the issue in Laradock.

12 вересня 2026 р. 9 хв читанняLaradock

While working locally with Laravel and Vite in ICanUp, I hit an error that initially looked like a Vite problem.

The dev server tried to update its cache under node_modules/.vite but could not remove one of the generated files.

The cause was simpler: the file had been created by another system user, and my normal project user did not have permission to modify it.

The error that started the investigation
Error: EACCES: permission denied, unlink'node_modules/.vite/deps/package.json'
In this case, EACCES did not mean Laravel or an npm package was broken. Vite simply lacked filesystem permission for a generated file it needed to replace.
Vite cache becomes root-owned after Docker or sudo and later fails with EACCES for the normal project user
The problem appears when one process creates node_modules/.vite as root and a later Vite run uses the normal project user.

I Checked File Ownership Before Debugging Vite

When an error contains EACCES, the filesystem is the first place I check.

There is no reason to immediately reinstall the entire frontend or change Vite configuration.

BASH - Inspect file ownership and permissions
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

If the project belongs to my normal user while node_modules/.vite suddenly contains root:root, the cause is already almost confirmed.

How Root-Owned Files Appear Inside node_modules

This state usually does not come from a normal npm run dev. It appears after mixing different ways of running Node.

For example, Vite may run once inside a container as root or through sudo, then later run again as the normal host user.

  • Vite or npm was executed with sudo.
  • A Docker container ran as root and wrote into a bind-mounted project directory.
  • Dependencies were installed by one user and the dev server was started by another.
  • Host Node and container Node alternated over the same node_modules tree without a consistent UID/GID contract.
How the user conflict appears
First run: root  |  vnode_modules/.vite/deps/package.jsonowner = root  Next run: normal user  |  vVite tries to unlink package.json  |  vEACCES

What Vite Stores in node_modules/.vite

The node_modules/.vite directory is not application source code.

Vite uses it for generated cache data and dependency pre-bundling so later development starts can be faster.

Deleting .vite does not remove application code or package definitions. Vite can generate the cache again.

Path

Purpose

Can be regenerated

resources/

Application source

No

package.json

Dependencies and scripts

No

package-lock.json

Locked dependency versions

No

node_modules/.vite

Generated Vite cache

Yes

If Only .vite Is Broken, Do Not Touch All of node_modules

I start with the smallest possible repair.

If node_modules generally belongs to the correct user and only the Vite cache is affected, removing that cache is enough.

BASH - Regenerate the Vite cache
rm -rf node_modules/.vite npm run dev
If .vite itself is owned by root and the normal user cannot remove its contents, a normal rm will fail as well. In that case, root is needed only once to clean the already incorrect files, not to run Vite.
BASH - Remove the root-owned cache once
sudo rm -rf node_modules/.vite npm run dev

The distinction matters: sudo is used only to remove an existing root-owned artifact.

The new npm run dev runs without sudo.

When Removing .vite Is No Longer Enough

Sometimes the problem is larger than one cache directory.

If npm install or another Node process previously ran as root, root-owned files can be scattered throughout node_modules.

BASH - Find root-owned files in node_modules
find node_modules \  -user root \  -print \  | head -n 50

If this returns many files, deleting .vite only removes the current symptom.

The dependency tree needs a consistent owner again.

BASH - Return node_modules to the current user
sudo chown -R \  "$(id -un)":"$(id -gn)" \  node_modules
I change ownership only where the problem has been confirmed. There is no reason to recursively chown the entire project directory without evidence.

A Clean Reinstall Is Another Safe Option

If ownership across node_modules is inconsistent or I no longer trust the dependency tree, rebuilding it can be cleaner than repairing individual files.

BASH - Reinstall dependencies with the correct user
sudo rm -rf node_modules npm ci

Again, sudo is used only to remove an existing root-owned tree. npm ci then runs as the normal project user.

This approach is appropriate when the project has an up-to-date package-lock.json.

Why chmod 777 Is Not a Fix

A permission error often tempts people to make the whole directory writable by everyone.

That hides the symptom but does not repair the ownership model.

BASH - A command I avoid
# Do not use this as a fix:chmod -R 777 node_modules

The right question is not "how do I let everyone write here?" but "which user should own these files, and why did root create them?".

Why sudo npm run dev Makes the Problem Worse

Running sudo npm run dev can temporarily hide EACCES because root is allowed to modify the root-owned cache.

But Vite then creates more files as root.

The next normal run reproduces the same problem.

How sudo creates a loop
Wrong recovery: EACCES  |  vsudo npm run dev  |  vmore root-owned files  |  vnormal npm run dev  |  vEACCES again
I do not run npm install, npm run dev, or Vite as root simply to bypass filesystem permissions.

Docker Adds UID and GID to the Ownership Problem

Inside Docker, the username does not need to match the username on the host. For a bind mount, the UID and GID of the process that creates the file are what matter.

If the container process has UID 0, files written into the project can appear as root-owned on the host.

BASH - Compare host and container users
id -uid -g docker compose exec workspace id

The service name may differ between Laradock setups, so I inspect the actual service where Node commands are executed.

In Laradock I Want One Stable Ownership Contract

The best solution is not to repair permissions after every run but to make every process create files under the correct user from the beginning.

For a bind-mounted project directory, the container user should align with the host UID/GID or follow another deliberately configured ownership model.

  • Do not run Node commands as root without a concrete reason.
  • Check the UID/GID of the user in the Node-capable container.
  • Do not mix a root container and the host user over the same node_modules tree.
  • If Node runs on the host, use the host user consistently.
  • If Node runs in a container, use the configured non-root container user consistently.
  • After changing Laradock configuration, clean old root-owned artifacts once.

Multiple PHP Versions Do Not Require Multiple Node Ownership Models

I run several PHP versions locally for different projects, using separate Laradock workspaces and wrapper commands.

Node filesystem ownership is a separate concern.

Even if PHP 8.3, 8.4, and 8.5 live in different services, frontend files for one project should still follow one predictable ownership contract.

I described the PHP side separately in my article about running PHP 8.3, 8.4, and 8.5 with Laradock, dphp, dartisan, and dcomposer.

Runtime Upgrades Can Accidentally Mix Node Users

During a runtime upgrade, it is easy to run some commands in a container, some on the host, and one command accidentally as root.

The ownership problem may only become visible later, when Vite tries to rebuild its cache.

I documented one of my larger environment upgrades in my PHP 8.5, Laravel, Laradock, and Composer package upgrade article. Changes like that are a good reminder that runtime versions and runtime users are separate configuration concerns.

My EACCES Recovery Order

What I find

What I do

Only .vite is root-owned

Remove node_modules/.vite and run Vite without sudo

Many files in node_modules are root-owned

Repair ownership or reinstall dependencies

The whole project is becoming root-owned

Find the process writing files as root instead of applying broad chmod

The problem returns after Docker

Inspect the container process UID/GID

How I Verify the Fix

BASH - Final ownership and Vite check
find node_modules \  -user root \  -print \  -quit npm run dev
  • Vite starts without sudo.
  • node_modules/.vite is created by the normal project user.
  • Restarting the dev server does not produce EACCES.
  • No new root-owned files appear in node_modules.
  • Docker or Laradock does not recreate the problem on the next run.
  • package.json and package-lock.json did not need to change for a permission repair.

What I Would Avoid

  • Do not run sudo npm run dev.
  • Do not use sudo npm install as a normal workflow.
  • Do not use chmod -R 777 node_modules.
  • Do not remove all of node_modules when only the Vite cache is affected.
  • Do not recursively chown the entire project without first identifying the problem.
  • Do not mix host Node and a root container over the same dependency tree.
  • Do not debug Laravel application code while the filesystem already shows root-owned generated files.

Vite EACCES often looks like a frontend problem, but it can be nothing more than a conflict between the user that created the cache and the user trying to update it.

What I Kept for Next Time

  • Check owner and permissions first when an error says EACCES.
  • Treat node_modules/.vite as generated cache that can be rebuilt.
  • Start with the smallest possible repair.
  • Use sudo only to clean already incorrect root-owned artifacts, not to run Node.
  • Do not replace an ownership problem with broad permissions.
  • Check the UID/GID of Docker processes writing into bind mounts.
  • Keep one stable ownership contract for frontend files in Laradock.
  • After a runtime upgrade, verify which user executed Node commands.

Conclusion

The EACCES: permission denied, unlink error did not require changes to Laravel or Vite configuration in my case.

Vite was simply trying to update generated cache while part of node_modules/.vite belonged to root.

In ICanUp, the real fix was restoring a consistent ownership contract: remove or repair root-owned artifacts and keep Node running under one agreed non-root user afterward.

The important fix is not teaching Vite to bypass permissions. It is stopping frontend files from being created by the wrong user.