Issue with setting environmental variable NODE_FUNCTION_ALLOW_EXTERNAL to two values simultanously

Describe the problem/error/question

I am trying to do two things, each of them works fine separately, but they do not work together due to problem with setting environmental variable.
First one is checking n8n version using code node:
const version = require(‘/usr/local/lib/node_modules/n8n/package’).version
return [
{
version_n8n: version
}
]

This code to work needs modules enabled by adding environmental variable in the docker compose file:

NODE_FUNCTION_ALLOW_EXTERNAL=*
All works fine at this stage.
Second, checking version of underling sqlite version:
const sqlite3 = require(‘sqlite3’);
const db = new sqlite3.Database(‘:memory:’);
return new Promise((resolve, reject) => {
db.get(‘SELECT sqlite_version() AS version’, (err, row) => {
if (err) return reject(err);
resolve([{ json: { version: row.version } }]);
});
});
This one needs “modules enabled” by adding environmental variable in the docker compose file:

NODE_FUNCTION_ALLOW_EXTERNAL=sqlite3. Again all work fine if this is not connected with the previous node.
Here comes the problem. Having both nodes in one workflow makes one to work improper.
Setting the variable twice, makes the latter one only to work:

NODE_FUNCTION_ALLOW_EXTERNAL=*

NODE_FUNCTION_ALLOW_EXTERNAL=sqlite3

Setting two values at once

NODE_FUNCTION_ALLOW_EXTERNAL=,sqlite3
makes the first code dependent on the setting "
" not to work.
Anyway strange that “" does not include “sqlite3” but found recently an explanation "sqlite3 requires compiled native binaries to communicate with operating system, whereas NODE_FUNCTION_ALLOW_EXTERNAL= is natively optimized to handle standard, pure-JavaScript packages”.
Anyway please advise how to set the environmental variable so both code nodes will run without issues.

What is the error message (if any)?

Please share your workflow

(Select the nodes on your canvas and use the keyboard shortcuts CMD+C/CTRL+C and CMD+V/CTRL+V to copy and paste the workflow.)

Share the output returned by the last node

Information on your n8n setup

  • n8n version: 2.22.5
  • Database (default: SQLite): SQLite
  • n8n EXECUTIONS_PROCESS setting (default: own, main): own
  • Running n8n via (Docker, npm, n8n cloud, desktop app): Docker (Docker Desktop on Windows 10)
  • Operating system: Windows 10, docker default os by latest Docker Desktop

@megrez7 quick check — are u on n8n 2.x with task runners enabled (default now)? if yes, NODE_FUNCTION_ALLOW_EXTERNAL has to be set on the runner service in ur docker-compose, not the main n8n service — Code nodes execute inside the runner container, so vars on the n8n service get ignored when runners are active. and on the value itself: drop the wildcard and list every module explicitly comma-separated (sqlite3,fs,...) — * is known buggy in 2.x and doesnt cover native compiled modules like sqlite3 regardless.

Thanks for response, but not sure what you exactly mean, I am completely newbie. Here is my docker compose file:

name: n8n
services:
n8n:
stdin_open: true
tty: true
container_name: n8n
ports:

  • 5678:5678
    environment:
  • GENERIC_TIMEZONE=Europe/Warsaw
  • TZ=Europe/Warsaw
  • N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
  • N8N_RUNNERS_ENABLED=true
  • NODE_FUNCTION_ALLOW_EXTERNAL=*,sqlite3
  • N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=false
    volumes:
  • n8n_data:/home/node/.n8n
    image: docker.n8n.io/n8nio/n8n

volumes:
n8n_data:
external: true
name: n8n_data

However wildcard work for the first code, so looks like it is understood by n8n

no worries — heres ur full compose file with the fixes applied, just copy-paste over what u have:

name: n8n
services:
  n8n:
    stdin_open: true
    tty: true
    container_name: n8n
    ports:
      - 5678:5678
    environment:
      - GENERIC_TIMEZONE=Europe/Warsaw
      - TZ=Europe/Warsaw
      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
      - N8N_RUNNERS_ENABLED=true
      - NODE_FUNCTION_ALLOW_EXTERNAL=n8n,sqlite3
    volumes:
      - n8n_data:/home/node/.n8n
    image: docker.n8n.io/n8nio/n8n
volumes:
  n8n_data:
    external: true
    name: n8n_data

what changed:

  • NODE_FUNCTION_ALLOW_EXTERNAL=*,sqlite3n8n,sqlite3 (explicit list, no wildcard — the * doesnt actually cover everything in 2.x and silently breaks combos)
  • removed the duplicate N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=false line (u had it twice, second one was overriding the first)

then in docker desktop: stop the container, run docker compose up -d again in ur n8n folder to apply the env changes. both code nodes should run in the same workflow after that.

if code 1 (the require('/usr/local/lib/node_modules/n8n/package') one) still complains about the n8n module being disallowed, alt approach is using fs to read the package.json instead — but try the above first.

Good point to clean up the fle. I have tried already explicitly setting both values but it does not work (tested again now) either.

The first node (checking n8n version) returns “Module ‘/usr/local/lib/node_modules/n8n/package’ is disallowed [line 1]”.

Wondering why wildcard works in this case, but “n8n” value not. Do we need anything more specific, or just some different value for NODE_FUNCTION_ALLOW_EXTERNAL?

yeah confirmed — the allowlist matches against the require string, and require('/usr/local/lib/node_modules/n8n/package') is an absolute path not a package name, so listing n8n doesnt match it. wildcard works because it bypasses the match entirely. cleanest fix is switch code 1 to use fs to read the package.json instead — then u only need sqlite3 in the external list and fs in the builtin list.

updated compose:

name: n8n
services:
  n8n:
    stdin_open: true
    tty: true
    container_name: n8n
    ports:
      - 5678:5678
    environment:
      - GENERIC_TIMEZONE=Europe/Warsaw
      - TZ=Europe/Warsaw
      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
      - N8N_RUNNERS_ENABLED=true
      - NODE_FUNCTION_ALLOW_EXTERNAL=sqlite3
      - NODE_FUNCTION_ALLOW_BUILTIN=fs
    volumes:
      - n8n_data:/home/node/.n8n
    image: docker.n8n.io/n8nio/n8n
volumes:
  n8n_data:
    external: true
    name: n8n_data

updated code 1 (version check via fs):

const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('/usr/local/lib/node_modules/n8n/package.json', 'utf8'));
return [{ json: { version_n8n: pkg.version } }];

code 2 (sqlite3) stays exactly as u had it. restart with docker compose up -d and both should run in the same workflow.

It works, thank you.

Just wondering, is there a way not to use fs but the original code approach?

Is this n8n bug?

Hi @megrez7

What about this?

name: n8n

services:
  n8n:
    image: docker.n8n.io/n8nio/n8n
    container_name: n8n
    restart: unless-stopped
    stdin_open: true
    tty: true
    ports:
      - "5678:5678"
    environment:
      # Timezone Settings
      - GENERIC_TIMEZONE=Europe/Warsaw
      - TZ=Europe/Warsaw
      
      # Runner Configuration
      # Enables internal runners to protect the main process from Code node crashes
      - N8N_RUNNERS_ENABLED=true
      
      # Module Permissions
      # The wildcard '*' allows both absolute paths (version check) 
      # and module names (sqlite3) simultaneously.
      - NODE_FUNCTION_ALLOW_EXTERNAL=*
      
      # Permissions & Security
      # Set to false to avoid permission conflicts on Windows/Docker Desktop volumes
      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=false
      
    volumes:
      - n8n_data:/home/node/.n8n

volumes:
  n8n_data:
    external: true
    name: n8n_data

another option besides fs or @kjooleng’s wildcard — just export the version as a plain env var in ur compose and read it via process.env, no require at all:

environment:
  - N8N_VERSION=2.22.5
  - NODE_FUNCTION_ALLOW_EXTERNAL=sqlite3

then code 1 becomes:

return [{ json: { version_n8n: process.env.N8N_VERSION } }];

trade-off is u bump the N8N_VERSION value manually when u upgrade n8n — but no require, no fs, no wildcard parsing edge cases. pick whichever feels cleanest for ur workflow.

Two things are biting you, and the fix is to drop * altogether.

  1. You can’t set the same env var twice — Docker keeps only the last one, so =* then =sqlite3 collapses to just =sqlite3, which is why the first node breaks.
  2. * is a wildcard only when it’s the entire value. The moment you make it a list (,sqlite3), n8n reads each entry as a literal package name, so * becomes a literal "" that matches nothing — that’s why the n8n-version node dies in the combined form.

Clean fix — one named allowlist, and require the n8n package by name instead of an absolute path:

  • Set a single value: NODE_FUNCTION_ALLOW_EXTERNAL=n8n,sqlite3
  • Change the first Code node from the absolute path to a module require:
    const version = require(‘n8n/package.json’).version;
    return [{ json: { version_n8n: version } }];

Now both packages are explicitly allowed by name, there’s no wildcard to conflict, and you’re not exposing every module to your Code nodes — which you don’t want from a security angle anyway.

Aside: NODE_FUNCTION_ALLOW_EXTERNAL is for npm packages; if you ever need a Node built-in (crypto, fs…), that’s the separate NODE_FUNCTION_ALLOW_BUILTIN var. sqlite3 is external, so you’re in the right place.

hello @megrez7

To obtain the n8n’s version, much easier to use Execute Command node with n8n --version command. However, this node is disabled by default.

As for the SQLite.. It’s generally not a good DB for production use, better to use Postgres (especially if you already have a Docker Compose), then you can use SELECT version(); from within the Postgres node