If the Execute Command node has vanished from your n8n instance, nothing is broken. Recent versions disable it by default because it runs arbitrary shell commands on the host with the same privileges as n8n itself.
The Execute Command node does exactly what the name says: it runs a shell command on the machine running n8n. That is useful, and it is also the single largest privilege boundary in a self-hosted install. Anyone who can edit a workflow can run anything the n8n process can run.
n8n’s answer was to stop shipping it enabled. Here is how to get it back if you genuinely need it, and the honest case for not doing so.
Table of contents
- What actually changed
- Re-enabling it with NODES_EXCLUDE
- What the node can and cannot see
- Why you probably want a different node
- If you do enable it, contain it
- The deployment shape that avoids the question
- How this fits the rest of the stack
- FAQ
What actually changed
Newer n8n releases disable the Execute Command node, and the Local File Trigger alongside it, by default. Cloud instances never had them. On self-hosted, they now sit behind an environment variable.
The symptom is that the node does not appear in the node picker, or an existing workflow that uses it fails with an unrecognised node type. Nothing was uninstalled — it is excluded at startup.
The reasoning is straightforward. n8n’s permission model treats workflow editing as a trusted action, but a lot of real deployments hand editor access to people who should not, transitively, have shell access to the server. The node collapsed those two things into one.
Re-enabling it with NODES_EXCLUDE
The control is the NODES_EXCLUDE environment variable, which holds a JSON array of node type names to disable. The default excludes the command node and the local file trigger. Setting it to an empty array re-enables everything:
NODES_EXCLUDE=[]
More conservatively, re-enable only the one you need and keep the file trigger excluded:
NODES_EXCLUDE=["n8n-nodes-base.localFileTrigger"]
Two things people get wrong here. The value must be valid JSON — a bare comma-separated list is ignored silently, which looks identical to the variable not being set. And the variable is read at startup only, so the instance has to restart before the node reappears.
Set it wherever your instance takes environment variables: the Docker Compose environment block, the Helm chart values, or the dashboard on a managed platform.
What the node can and cannot see
The command runs in the default shell of the machine running n8n, with n8n’s user and n8n’s filesystem view. In Docker, that means inside the container — not on the host, and not in any other container.
This surprises people in both directions. A command that works on the host fails in the container because the binary was never installed in the image. And a command that looks dangerous is often contained by the fact that the container has almost nothing in it.
Multiple commands work either chained with && on one line, or written on separate lines:
cd /data && ls -la
The node returns stdout, stderr and exitCode. Check exitCode explicitly with an IF node. A command that fails still produces output, and a workflow that only reads stdout will happily treat a failure as a success.
Why you probably want a different node
Most Execute Command uses fall into a handful of categories, and almost all of them have a purpose-built alternative that does not require shell access:
- Calling curl — use the HTTP Request node. It handles auth, retries, pagination and error output properly, none of which you get by parsing curl’s stdout.
- Reading or writing files — use Read/Write Files From Disk. Scoped to a path, and it returns binary data in the shape the rest of n8n expects.
- Data transformation with jq or sed — use a Code node. JavaScript in-process beats shelling out to parse text.
- Running a script you wrote — put the script behind an HTTP endpoint and call it. That gives you a real interface, independent scaling and its own logs.
The last one is the pattern worth internalising. If a workflow needs to run your code, deploy the code as a small service with an endpoint rather than smuggling it into a shell string. It becomes testable, versioned, deployable on its own, and it stops your automation tool from needing shell privileges.
If you do enable it, contain it
There are legitimate cases — a self-hosted instance, one operator, a binary that has no API. If that is you, a few things make the blast radius smaller:
- Run n8n as a non-root user in the container. Many images still default to root.
- Never interpolate workflow data straight into the command string.
rm -rf {{$json.path}}is a shell injection waiting for a malformed webhook. - Keep the file trigger excluded even when the command node is enabled — the two combined are a considerably larger surface than either alone.
- Restrict who can edit workflows. Editor access is now equivalent to shell access on that host.
- Check
exitCodeon every execution and route non-zero to an error branch.
The second point deserves emphasis. Command injection through a workflow expression is the realistic way this gets exploited, and it does not require anyone to have compromised your instance — just to have sent you a webhook payload you did not sanitise.
The deployment shape that avoids the question
The reason the Execute Command node feels necessary is usually that the automation and the custom code are expected to live on the same box. Split them and the need evaporates: n8n orchestrates, a small service does the work, and they talk over HTTP.
That is two deployables instead of one, which is only a burden if each deployable is a burden. On RunxBuild, n8n runs as a managed tool with its own plan and environment variables, and the service it calls is a repository you push — Node, Python, Go, Docker, whatever the script is written in — with a build log, a live route and rollback to the previous deploy. Neither one needs shell access to the other.
How this fits the rest of the stack
The Execute Command node being disabled by default is a small inconvenience that prevents a large class of mistakes, and the workflows that reach for it are usually one refactor away from not needing it. Splitting orchestration from execution means running an n8n instance and a small service next to it, which is a cost worth knowing up front — the RunxBuild hosting calculator shows the tool plan and the service plan as separate line items so the trade is visible.
Useful related references:
- n8n AI Agent Node: What It Does and When a Plain Chain Is Better
- n8n + Qdrant: A Vector Search Node for Real Workflows
- n8n HTTP Request Node: The Auth and Error Playbook
- Node services on RunxBuild
FAQ
Why is the Execute Command node missing from my n8n?
Recent versions exclude it by default, along with the Local File Trigger, because it runs arbitrary shell commands with n8n’s own privileges. Nothing is uninstalled — set the NODES_EXCLUDE environment variable to an empty JSON array and restart to bring it back.
How do I set NODES_EXCLUDE?
It takes a JSON array of node type names, for example NODES_EXCLUDE=[] to disable nothing. Set it in your Docker Compose environment block, Helm values, or the dashboard on a managed platform. The value must be valid JSON, and n8n reads it only at startup, so restart afterwards.
Does the Execute Command node run on the host or in the container?
In the container, with n8n’s user and filesystem. Commands that work on the host often fail because the binary is not in the image. It cannot reach the host or other containers unless you have explicitly mounted or shared something.
Is the Execute Command node safe to enable?
Only when workflow-edit access and shell access on that host are acceptable to grant to the same people. The realistic risk is command injection through an unsanitised expression such as an interpolated webhook field, so never build command strings from workflow data.
What should I use instead?
The HTTP Request node for anything you would curl, Read/Write Files From Disk for file work, a Code node for text transformation, and a small deployed service with an HTTP endpoint for custom scripts. The last one gives you versioning, tests and independent logs.