Task Runners & Code Execution
Relevant source files
The following files were used as context for generating this wiki page:
- packages/@n8n/task-runner/src/task-runner.ts
- packages/@n8n/task-runner-python/src/main.py
- packages/cli/src/task-runners/task-broker/task-broker.service.ts
- packages/nodes-base/nodes/Code/Code.node.ts
- packages/cli/test/integration/task-runners/js-task-runner-execution.integration.test.ts
- packages/cli/src/task-runners/task-runner-module.ts
- packages/cli/src/task-runners/task-managers/local-task-requester.ts
The Task Runner system in n8n provides a sandboxed environment for executing user-provided code, primarily from the Code node. It is designed to run code in different languages (like JavaScript and Python) in isolated processes, enhancing security and stability. This system ensures that custom code execution does not interfere with the main n8n application process.
The architecture consists of three main components: the initiating node (e.g., Code node), the TaskBrokerService which manages runner processes, and the language-specific Task Runners (e.g., JS and Python runners) that execute the code.
Architecture Overview
The overall flow begins when a node, such as the Code node, needs to execute custom code. It sends a request to the TaskBrokerService via a task requester. The broker then forwards this task to an available and appropriate language-specific runner. The runner executes the code and sends the result back through the broker to the originating node.
This diagram illustrates the high-level interaction between the components.
Key Components
| Component | Description | Source File |
|---|---|---|
| Code Node | An n8n node that allows users to write and execute custom JavaScript or Python code as part of a workflow. It initiates the task execution request. | packages/nodes-base/nodes/Code/Code.node.ts |
| TaskRunnerModule | Manages the lifecycle of the task runner system, including starting and stopping the TaskBrokerService. | packages/cli/src/task-runners/task-runner-module.ts |
| LocalTaskRequester | A client within the main n8n process that communicates with the TaskBrokerService to request task execution. | packages/cli/src/task-runners/task-managers/local-task-requester.ts |
| TaskBrokerService | A central service that manages a pool of task runner child processes. It receives tasks and delegates them to available runners. | packages/cli/src/task-runners/task-broker/task-broker.service.ts |
| JS Task Runner | A child process dedicated to executing JavaScript code in a sandboxed environment. | packages/@n8n/task-runner/src/task-runner.ts |
| Python Task Runner | A child process dedicated to executing Python code. | packages/@n8n/task-runner-python/src/main.py |
Task Execution Flow
The sequence diagram below details the step-by-step process of executing a task from the Code node.
Code Node Initiation
When a workflow executes a Code node, the execute method determines the language and prepares the task. It then uses the LocalTaskRequester to start the task.
// packages/nodes-base/nodes/Code/Code.node.ts
// Simplified example of task initiation
const taskRequester = this.getTaskRequester();
const result = await taskRequester.startTask<INodeExecutionData[], Error>(
// ... arguments
);
if (!result.ok) {
throw new NodeOperationError(this.getNode(), result.error.message, {
// ... error details
});
}
Sources: packages/nodes-base/nodes/Code/Code.node.ts:1210-1232
Task Broker Service
The TaskBrokerService is responsible for managing the lifecycle of runner processes. When a task is received, it finds an available runner for the specified language or spawns a new one if the pool is not full. It communicates with the runners via Inter-Process Communication (IPC).
Sources: packages/cli/src/task-runners/task-broker/task-broker.service.ts
Language-Specific Runners
JavaScript Task Runner
The JS Task Runner executes JavaScript code within a vm2 sandbox. This provides a secure environment that restricts access to the host system's file system and certain Node.js modules.
Security and Module Access
The ability to require built-in and external Node.js modules is controlled by environment variables. This is a critical security feature to prevent malicious code execution.
| Environment Variable | Description | Example |
|---|---|---|
NODE_FUNCTION_ALLOW_BUILTIN | A comma-separated list of allowed Node.js built-in modules. | crypto,path |
NODE_FUNCTION_ALLOW_EXTERNAL | A comma-separated list of allowed npm packages. | moment,lodash |
An integration test demonstrates this behavior:
require('crypto')succeeds whenNODE_FUNCTION_ALLOW_BUILTINincludescrypto.require('fs')fails as it's not in the allow-list, preventing file system access.require('moment')succeeds whenNODE_FUNCTION_ALLOW_EXTERNALincludesmoment.require('lodash')fails as it's not in the allow-list.
Sources: packages/cli/test/integration/task-runners/js-task-runner-execution.integration.test.ts:203-263
Python Task Runner
The Python Task Runner executes Python code in a separate process. It communicates with the TaskBrokerService over stdin and stdout, receiving tasks and sending back results as JSON-encoded strings.
The main loop in main.py continuously listens for input, parses the task data, executes the Python code using exec, and prints the result to stdout.
# packages/@n8n/task-runner-python/src/main.py:101-113
def main():
"""Main loop to read, execute and return tasks."""
while True:
try:
task = read_task()
if not task:
break
result = execute_task(task)
write_result(result)
except Exception as e:
error_result = {"ok": False, "error": str(e)}
write_result(error_result)
Sources: packages/@n8n/task-runner-python/src/main.py:101-113
The execute_task function prepares the execution context, including input data (items) and a helper function (get_workflow_static_data), and then runs the user's code.
Sources: packages/@n8n/task-runner-python/src/main.py:65-99
Configuration
The Task Runner system is configured through the TaskRunnersConfig. Key settings include:
| Setting | Description |
|---|---|
mode | The execution mode for task runners. The integration test uses 'internal'. |
enabled | A boolean to enable or disable the task runner feature. |
port | The port on which the TaskBrokerService listens. |
isNativePythonRunnerEnabled | A boolean to enable or disable the Python runner. |
These settings are typically managed within the n8n configuration files and are demonstrated in the setup of the integration tests.
Sources: packages/cli/test/integration/task-runners/js-task-runner-execution.integration.test.ts:24-28
Summary
The Task Runner and Code Execution system is a cornerstone of n8n's extensibility, allowing users to run custom logic in a secure and isolated manner. By offloading code execution to separate processes managed by a central broker, n8n supports multiple programming languages like JavaScript and Python while protecting the main application from potential crashes or security vulnerabilities in user-provided code. The configurable security model for module imports in the JS runner further hardens the system against malicious use.