Solved: “The file is not writable” in Read/Write Files from Disk — even with 777 permissions
TL;DR
If you’re getting The file "/path/to/file" is not writable from the Read/Write Files from Disk node in n8n 2.0+, and you’ve already confirmed permissions are correct (even chmod 777, even writing to the exact same path manually with docker exec ... touch succeeds) — the problem is almost certainly not a filesystem permission issue.
Starting in n8n 2.0, the N8N_RESTRICT_FILE_ACCESS_TO environment variable defaults to ~/.n8n-files. Any path outside that allow-list gets rejected by the node itself, before it ever touches the filesystem. On top of that, n8n reserves /home/node/.n8n/ for its own internal state (database, config, logs) and blocks writes into it by default via N8N_BLOCK_FILE_ACCESS_TO_N8N_FILES. If your target path lives anywhere under .n8n/, it will be blocked regardless of Unix permissions.
The misleading part: the error message says “is not writable”, which sends most people straight to checking chmod/chown — when the actual cause is an application-level allow-list check, not a kernel-level permission check.
How I diagnosed it (so you can verify your own case)
I hit this error with a straightforward setup: a workflow builds an .xlsx file and writes it with Read/Write Files from Disk to:
/home/node/.n8n/files/output.xlsx
Error returned by the node:
{
"errorMessage": "The file \"/home/node/.n8n/files/output.xlsx\" is not writable.",
"n8nDetails": {
"nodeName": "Read/Write Files from Disk",
"nodeType": "n8n-nodes-base.readWriteFile",
"operation": "write",
"n8nVersion": "2.29.7 (Self Hosted)"
}
}
Step 1 — Check ownership and permissions of the target directory
docker exec -it n8n-n8n-1 ls -la /home/node/.n8n/files/
Result: the directory was drwxrwxrwx, owned by node:node. Fully open. Not the issue.
Step 2 — Confirm which user the n8n process actually runs as
docker exec -it n8n-n8n-1 sh -c "ps aux | grep node"
Confirmed: the main n8n process runs as node, the same user that owns the target directory.
Step 3 — The decisive test: write to the exact same path manually, as the same user
docker exec -u node -it n8n-n8n-1 sh -c "touch /home/node/.n8n/files/output/test.xlsx && echo WRITE_OK"
Result: WRITE_OK. The file was created successfully with node:node ownership.
This is the key diagnostic step. If a manual write to the exact path (as the exact user the n8n process runs as) succeeds, but the node still throws “not writable” on the same path, the filesystem is not the problem. Something inside n8n’s own write logic is rejecting the path before attempting the actual fs.write call.
Step 4 — Confirm against n8n’s own documentation and issue tracker
This turned out to be a well-known, widely reported pattern — not a one-off bug. Multiple independent reports on GitHub and the n8n community forum describe the identical symptom: manual touch/echo inside the container works, but the node fails with “not writable” on the same path (see references at the end).
n8n’s official docs confirm the actual mechanism:
Starting in n8n 2.0,
N8N_RESTRICT_FILE_ACCESS_TOdefaults to~/.n8n-files. To allow file operations elsewhere, set the variable explicitly.
n8n reserves the
/home/node/.n8n/directory for its internal state. Don’t write your own files there.
Note the easy-to-miss detail: the default allowed directory is .n8n-files (a sibling of .n8n), not a subfolder inside .n8n. /home/node/.n8n/files/ looks similar but is a completely different, blocked location.
The fix
You have two viable options depending on whether you need persistence across container rebuilds.
Option A — Quick fix, no docker-compose changes needed
Create the default allowed directory inside the running container:
docker exec -u node -it n8n-n8n-1 mkdir -p /home/node/.n8n-files
Then in the Read/Write Files from Disk node, set the path to:
/home/node/.n8n-files/output.xlsx
Caveat: this directory is not mounted as a volume, so it lives only in the container’s writable layer. It will be wiped out on the next docker compose down / container recreation. Fine for quick testing, not fine for production.
Option B — Persistent fix (recommended)
Mount a dedicated host directory outside .n8n, and explicitly whitelist it:
version: "3.8"
services:
n8n:
image: n8nio/n8n:latest
ports:
- "5678:5678"
environment:
- GENERIC_TIMEZONE=Asia/Tehran
- N8N_ENCRYPTION_KEY=your-strong-random-key-here
- N8N_RESTRICT_FILE_ACCESS_TO=/home/node/files
volumes:
- ./n8n_data:/home/node/.n8n
- ./files:/home/node/files
restart: unless-stopped
The important line is:
- N8N_RESTRICT_FILE_ACCESS_TO=/home/node/files
Without it, even a correctly mounted volume outside .n8n will still be blocked, because the allow-list defaults to ~/.n8n-files only — any other path (mounted volume or not) is rejected unless you explicitly add it here.
Apply with:
docker compose down
docker compose up -d
Then in the node, use:
/home/node/files/output.xlsx
This keeps n8n’s internal state (.n8n/) and your workflow’s file outputs (./files) in two separate, independently persistent volumes — which is also n8n’s own recommended pattern, since they explicitly say not to write custom files into .n8n/.
Why this trips people up
- The error message text (“not writable”) strongly implies a Unix permission problem, not an application-level access-control rejection. Nothing in the message hints at an allow-list.
N8N_RESTRICT_FILE_ACCESS_TOis a relatively recent default (n8n 2.0+). Anyone following older tutorials, older forum answers, or their own memory from pre-2.0 setups will not expect this variable to matter at all./home/node/.n8n/files/looks like a completely reasonable, intentional location — it’s easy to assume “files” is meant for exactly this purpose, when in fact it’s inside the reserved.n8n/state directory.- Manual filesystem tests (
touch,echo >,ls -la) all pass, which — reasonably — rules out permissions in most people’s mental model, but doesn’t rule out n8n’s own internal path check.
If you’re debugging a similar case, the fastest way to tell these two failure modes apart is exactly the test from Step 3 above: try writing to the exact same path manually, as the exact user n8n’s process runs as. If that succeeds and the node still fails, stop looking at chmod/chown and check N8N_RESTRICT_FILE_ACCESS_TO and N8N_BLOCK_FILE_ACCESS_TO_N8N_FILES instead.
Environment this was confirmed on
- n8n
2.29.7(Self Hosted, Docker) n8n-nodes-base.readWriteFile, node version1.1- Docker Desktop on Windows
- Binary data mode:
filesystem
References
- n8n docs — Read/Write Files from Disk
- n8n docs — v2.0 breaking changes
- GitHub issue #23318 — Read/Write Files from Disk can not save file
- GitHub issue #24829 — Read/Write Files from Disk fails with “not writable”
- GitHub issue #23646 — “not writable” error despite correct permissions
- GitHub issue #23934 — “not writable” after upgrading n8n
- n8n Community — “[Help] Read/Write Files node says ‘not writable’, but Execute Command (touch) works fine”
If you’re hitting this and the fix above doesn’t resolve it, double-check:
- Your n8n version is actually 2.0+ (this default didn’t exist before)
- You restarted/recreated the container after changing the environment variable (env var changes don’t apply to a running container)
- The path in the node exactly matches the path you whitelisted — no trailing slash mismatches, no relative paths