This error occurs when a Node.js application attempts to write to a file or directory on a read-only filesystem. Common in containerized environments, serverless platforms, and systems with intentionally read-only root filesystems.
The EROFS error (Error Read-Only File System) indicates that Node.js attempted to perform a write operation on a filesystem that is mounted as read-only. This is a system-level restriction where the operating system prevents any modifications to the filesystem. This error is increasingly common in modern deployment environments. Docker containers often run with read-only root filesystems as a security best practice. Serverless platforms like Vercel, AWS Lambda, and Netlify Functions mount the application code as read-only, allowing writes only to specific temporary directories. Additionally, embedded systems and IoT devices may use read-only filesystems to prevent corruption. The error occurs when your Node.js code attempts operations like fs.writeFile(), fs.mkdir(), or when npm tries to create symlinks or cache directories during runtime. The underlying filesystem simply refuses these write operations, causing Node.js to throw this error.
Check your error stack trace to determine exactly where the write is being attempted:
Error: EROFS: read-only file system, write
at Object.writeFileSync (node:fs:2129:3)
at /app/src/cache.js:45:8Look for operations like:
- fs.writeFile(), fs.writeFileSync()
- fs.mkdir(), fs.mkdirSync()
- fs.appendFile()
- Package manager operations (npm, yarn)
Change your code to write to the /tmp directory, which is writable in most environments:
const fs = require('fs');
const path = require('path');
// ❌ Don't write to application directory
// fs.writeFileSync('./cache/data.json', data);
// ✅ Write to /tmp instead
const tmpPath = path.join('/tmp', 'data.json');
fs.writeFileSync(tmpPath, data);In serverless environments like AWS Lambda or Vercel, /tmp is the only writable location and has storage limits (512MB in Lambda).
If running in Docker, mount tmpfs volumes for directories that need to be writable:
# docker-compose.yml
services:
app:
image: node:20
read_only: true
tmpfs:
- /tmp
- /app/.npm
- /app/node_modules/.cacheOr in Kubernetes:
apiVersion: v1
kind: Pod
spec:
containers:
- name: app
image: node:20
securityContext:
readOnlyRootFilesystem: true
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /app/.cache
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}For persistent data, use external storage instead of filesystem writes:
// Instead of file-based caching:
// fs.writeFileSync('./cache.json', JSON.stringify(data));
// Use Redis, DynamoDB, or environment variables:
const redis = require('redis');
const client = redis.createClient();
await client.set('cache-key', JSON.stringify(data));For configuration, use environment variables:
// Instead of writing config files:
// fs.writeFileSync('./config.json', config);
// Read from environment:
const config = {
apiKey: process.env.API_KEY,
endpoint: process.env.API_ENDPOINT
};If npm or yarn is causing EROFS errors during runtime, configure cache to use /tmp:
# Set npm cache location
npm config set cache /tmp/.npm
# Or use environment variable
export npm_config_cache=/tmp/.npmIn your Dockerfile:
ENV npm_config_cache=/tmp/.npm
ENV NODE_OPTIONS="--max-old-space-size=4096"
# Install dependencies at build time, not runtime
RUN npm ci --only=productionIf you have root access and the read-only state is unintentional (disk errors, emergency mode), remount as read-write:
# Check current mount status
mount | grep "on / "
# Remount root filesystem as read-write
sudo mount -o remount,rw /
# Or for specific mount point
sudo mount -o remount,rw /data⚠️ Warning: Only do this on systems you control. In containerized/serverless environments, read-only filesystems are intentional security measures.
Serverless Platform Specifics:
- AWS Lambda: /tmp has 512MB limit and persists only for the lifetime of the execution environment
- Vercel: Only /tmp is writable; all other paths are read-only
- Netlify Functions: Similar to AWS Lambda, use /tmp for temporary storage
- Google Cloud Functions: /tmp is mounted as tmpfs with instance memory limit
Security Benefits of Read-Only Filesystems:
Read-only root filesystems prevent attackers from modifying application code or installing malware if they gain access. This is why Kubernetes security policies often require readOnlyRootFilesystem: true. Design your applications to be stateless and write to external storage instead.
Detecting Filesystem Status in Code:
const fs = require('fs');
const path = require('path');
function isWritable(directory) {
const testFile = path.join(directory, '.write-test');
try {
fs.writeFileSync(testFile, 'test');
fs.unlinkSync(testFile);
return true;
} catch (error) {
return false;
}
}
console.log('/ writable:', isWritable('/'));
console.log('/tmp writable:', isWritable('/tmp'));Build vs Runtime Considerations:
Distinguish between build-time and runtime writes. During Docker image builds, the filesystem is writable, so npm install works. At runtime with read-only containers, any post-build file writes will fail. Ensure all dependencies are installed during the build phase.
Error: Listener already called (once event already fired)
EventEmitter listener already called with once()
Error: EACCES: permission denied, open '/root/file.txt'
EACCES: permission denied
Error: Invalid encoding specified (stream encoding not supported)
How to fix Invalid encoding error in Node.js readable streams
Error: EINVAL: invalid argument, open
EINVAL: invalid argument, open
TypeError: readableLength must be a positive integer (stream config)
TypeError: readableLength must be a positive integer in Node.js streams