All posts
nodejsfilesystem

Fixing "EACCES: permission denied" in Node.js

Why Node.js throws EACCES for filesystem and network operations, common causes, and how to fix each without unsafe workarounds.

SR

Suhail Roushan

August 6, 2026

·
5 min read
·
0 views

EACCES: permission denied is a straightforward permissions error — the operating system rejected an operation because the user running your Node.js process lacks the necessary access rights for the specific file, directory, or (less commonly) network port involved — and unlike EPERM's broader ambiguity, EACCES almost always means exactly what it says: a genuine permission restriction.

This error means the OS-level permission check for a filesystem or network operation failed for the user your Node.js process is running as — commonly writing to a directory owned by a different user, executing a file without execute permission, or binding to a privileged network port without sufficient privileges.

Why This Error Happens

Every file, directory, and (on Unix-like systems) low-numbered network port has associated permissions restricting which users can read, write, execute, or bind to it. EACCES fires when your Node.js process's effective user doesn't have the required permission for the specific operation attempted — this is the OS correctly enforcing its permission model, and the fix should generally be adjusting the actual permissions or running as the correct user, not bypassing the check.

Reproducing the Error

Writing to a directory the current user doesn't own:

fs.writeFileSync("/var/log/myapp/output.log", "log data");
// Error: EACCES: permission denied, open '/var/log/myapp/output.log'
// (if the running user doesn't have write access to that directory)

Binding to a privileged port (below 1024) without elevated privileges:

const server = http.createServer(app);
server.listen(80);
// Error: listen EACCES: permission denied 0.0.0.0:80
// (non-root users can't bind to ports below 1024 on most Unix systems)

Core Concepts Behind This Error

Privileged ports (below 1024) require elevated privileges to bind to on Unix-like systems — this is a long-standing OS-level restriction, not a Node.js limitation, and the standard production pattern is running your application on a higher, unprivileged port and using a reverse proxy (nginx, a load balancer) to forward traffic from port 80/443, rather than running your application process with root privileges.

Directory and file ownership mismatches are common after deployment processes that run as a different user than your application's runtime process — a build step running as one user creating files, followed by your application running as a different, more restricted user attempting to write to those same files, produces this error even though "someone" clearly has access.

npm global installs commonly hit EACCES when the global npm directory requires elevated permissions, similar to the npm-specific case discussed for EPERM — the standard fix is reconfiguring npm's global prefix to a user-owned directory, not running npm with sudo.

Docker containers running as a non-root user (a security best practice) can produce EACCES for volumes or files that weren't given matching ownership/permissions during the container build or run configuration — this is a common source of confusion since the application code is identical to a working local setup, but the containerized user's actual permissions differ.

Fixing "EACCES: Permission Denied"

Fix 1: For privileged port binding, run on a higher port and use a reverse proxy for the actual public-facing port:

server.listen(3000); // unprivileged port
server {
    listen 80;
    location / {
        proxy_pass http://localhost:3000;
    }
}

Fix 2: Adjust file/directory ownership or permissions to match the actual user running your application, rather than running with elevated privileges:

chown -R appuser:appuser /var/log/myapp
chmod -R u+w /var/log/myapp

Fix 3: For npm global install permission issues, reconfigure npm's global directory to a location your user owns:

npm config set prefix ~/.npm-global
# add ~/.npm-global/bin to PATH

Fix 4: For Docker containers, explicitly set matching ownership on volumes and application directories during the build or via an entrypoint script:

RUN mkdir -p /app/data && chown -R node:node /app/data
USER node

Should You Ever Run a Node.js Process as Root to Avoid EACCES?

Almost never in production — running as root to sidestep a permission restriction removes a meaningful security boundary; if your process is ever compromised, it then has root-level access to the entire system rather than being contained to its own limited permissions. Fix the actual ownership/permission structure, or use a reverse proxy for privileged port binding, rather than escalating your application's privileges as a workaround.

Preventing This Error in Production

Design your deployment process so files and directories your application needs to access are owned by (or have appropriate permissions for) the actual user your application runs as, verified as part of your deployment or container build process rather than discovered through a runtime EACCES error. Use a reverse proxy for binding to privileged ports rather than running your application process with elevated privileges, keeping your actual application process running with the minimum permissions it genuinely needs.

If you hit this error, check which specific user your process runs as and what permissions that user actually has on the target resource — the fix is almost always aligning ownership/permissions correctly, not escalating privileges to bypass the check.

Related posts

Written by Suhail Roushan — Full-stack developer. More posts on AI, Next.js, and building products at suhailroushan.com/blog.

Get in touch