n8n - 💡(How to fix) Fix External task-runner sandbox ignores NODE_FUNCTION_ALLOW_EXTERNAL — modules always 'disallowed' (n8n 2.14.2)

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

In N8N_RUNNERS_MODE: external with n8nio/runners image, the env var NODE_FUNCTION_ALLOW_EXTERNAL is not honored by the task-runner sandbox. Code nodes calling require('ioredis') (or any whitelisted external module) fail with Module 'ioredis' is disallowed [line 2] even when:

  • The module is physically installed in /home/runner/node_modules/ioredis (verified via docker compose exec n8n-task-runner node -e "console.log(typeof require('ioredis'))"function)
  • NODE_FUNCTION_ALLOW_EXTERNAL=ioredis is set on the n8n-task-runner service (verified via docker compose exec n8n-task-runner env | grep NODE_FUNCTION)
  • NODE_FUNCTION_ALLOW_EXTERNAL=* wildcard also fails
  • Containers were force-recreated after env var changes

Switching to N8N_RUNNERS_MODE: internal (with the env var on the n8n main service) immediately fixes the issue — the same Code node works.

Root Cause

In N8N_RUNNERS_MODE: external with n8nio/runners image, the env var NODE_FUNCTION_ALLOW_EXTERNAL is not honored by the task-runner sandbox. Code nodes calling require('ioredis') (or any whitelisted external module) fail with Module 'ioredis' is disallowed [line 2] even when:

  • The module is physically installed in /home/runner/node_modules/ioredis (verified via docker compose exec n8n-task-runner node -e "console.log(typeof require('ioredis'))"function)
  • NODE_FUNCTION_ALLOW_EXTERNAL=ioredis is set on the n8n-task-runner service (verified via docker compose exec n8n-task-runner env | grep NODE_FUNCTION)
  • NODE_FUNCTION_ALLOW_EXTERNAL=* wildcard also fails
  • Containers were force-recreated after env var changes

Switching to N8N_RUNNERS_MODE: internal (with the env var on the n8n main service) immediately fixes the issue — the same Code node works.

Fix Action

Workaround

Switch to N8N_RUNNERS_MODE: internal, set NODE_FUNCTION_ALLOW_EXTERNAL=ioredis and NODE_FUNCTION_ALLOW_BUILTIN=* on the n8n main service, force-recreate the n8n container. The same Code node then succeeds with { ok: true, type: 'function' }.

Code Example

environment:
     NODE_FUNCTION_ALLOW_EXTERNAL: ioredis
     NODE_FUNCTION_ALLOW_BUILTIN: "*"

---

ARG N8N_VERSION=latest
   FROM node:22-alpine AS builder
   RUN npm install --prefix /opt/extra-modules ioredis@5.4.1
   FROM n8nio/runners:${N8N_VERSION}
   COPY --from=builder /opt/extra-modules/node_modules /home/runner/node_modules

---

try {
     const Redis = require('ioredis');
     return [{ json: { ok: true, type: typeof Redis } }];
   } catch (e) {
     return [{ json: { ok: false, err: e.message } }];
   }
RAW_BUFFERClick to expand / collapse

Summary

In N8N_RUNNERS_MODE: external with n8nio/runners image, the env var NODE_FUNCTION_ALLOW_EXTERNAL is not honored by the task-runner sandbox. Code nodes calling require('ioredis') (or any whitelisted external module) fail with Module 'ioredis' is disallowed [line 2] even when:

  • The module is physically installed in /home/runner/node_modules/ioredis (verified via docker compose exec n8n-task-runner node -e "console.log(typeof require('ioredis'))"function)
  • NODE_FUNCTION_ALLOW_EXTERNAL=ioredis is set on the n8n-task-runner service (verified via docker compose exec n8n-task-runner env | grep NODE_FUNCTION)
  • NODE_FUNCTION_ALLOW_EXTERNAL=* wildcard also fails
  • Containers were force-recreated after env var changes

Switching to N8N_RUNNERS_MODE: internal (with the env var on the n8n main service) immediately fixes the issue — the same Code node works.

Environment

  • n8n version: 2.14.2 (verified via docker compose exec n8n n8n --version)
  • Runner image: n8nio/runners:latest
  • Deployment: docker-compose, self-hosted
  • Stack: n8n + n8n-task-runner (external) + postgres + redis + traefik

Minimal repro

  1. docker-compose.yml with n8n and n8n-task-runner services.
  2. Set on n8n-task-runner:
    environment:
      NODE_FUNCTION_ALLOW_EXTERNAL: ioredis
      NODE_FUNCTION_ALLOW_BUILTIN: "*"
  3. Build a custom runner image that installs ioredis (the n8nio/runners image doesn't ship npm):
    ARG N8N_VERSION=latest
    FROM node:22-alpine AS builder
    RUN npm install --prefix /opt/extra-modules [email protected]
    FROM n8nio/runners:${N8N_VERSION}
    COPY --from=builder /opt/extra-modules/node_modules /home/runner/node_modules
  4. docker compose up -d --force-recreate n8n n8n-task-runner
  5. Verify env var is in container: docker compose exec n8n-task-runner env | grep NODE_FUNCTION
  6. Verify module loads via Node: docker compose exec n8n-task-runner node -e "console.log(typeof require('ioredis'))"function
  7. Create a Code node WF with:
    try {
      const Redis = require('ioredis');
      return [{ json: { ok: true, type: typeof Redis } }];
    } catch (e) {
      return [{ json: { ok: false, err: e.message } }];
    }
  8. Execute → { ok: false, err: "Module 'ioredis' is disallowed [line 2]" }

Expected behavior

The Code node should successfully load ioredis (since the module is installed and the env var allows it) and return { ok: true, type: 'function' }.

Actual behavior

Sandbox rejects with Module 'ioredis' is disallowed [line 2], suggesting the allowlist is empty in the runner sandbox's view despite the env var being present in the process environment.

Workaround

Switch to N8N_RUNNERS_MODE: internal, set NODE_FUNCTION_ALLOW_EXTERNAL=ioredis and NODE_FUNCTION_ALLOW_BUILTIN=* on the n8n main service, force-recreate the n8n container. The same Code node then succeeds with { ok: true, type: 'function' }.

Hypothesis

It seems the broker on n8n main does NOT propagate the sandbox allowlist config to the external runner process. The launcher subprocess that spawns the actual JS runner reads its own env, but the per-task config received via the broker WebSocket may not include or may override the allowlist.

A look at:

  • packages/@n8n/task-runner-launcher (Go) — how it spawns the child runner
  • packages/@n8n/task-runner (TypeScript) — how it reads NODE_FUNCTION_ALLOW_EXTERNAL
  • The broker → runner protocol (/runners endpoint) — what config payload is sent per task

…would probably show where the allowlist is dropped.

Additional context

  • The n8nio/runners image being slim (no npm) makes it harder to debug since you can't npm ls -g ioredis to check resolution. The user needs a multi-stage Dockerfile.
  • The n8nio/runners image does not include a node user entry in /etc/passwd, so a USER node directive in a derived Dockerfile fails with unable to find user node: no matching entries in passwd file.
  • Documentation https://docs.n8n.io/hosting/configuration/task-runners/ describes setting NODE_FUNCTION_ALLOW_EXTERNAL on the task runner but does not mention the propagation chain or the slim image limitations.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

The Code node should successfully load ioredis (since the module is installed and the env var allows it) and return { ok: true, type: 'function' }.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING