Self-Host n8n AI: Sandboxed Code Execution and Private Search

Self-Host n8n AI: Sandboxed Code Execution and Private Search

An AI assistant that writes code for you raises an awkward follow-up question: where does that code run?

For a long time the honest answer in most automation tools was "in the same process as everything else, and we hope for the best." That answer aged badly. Earlier this month a type confusion flaw in isolated-vm, the sandbox library a lot of platforms lean on for untrusted JavaScript, turned sandboxed code into host memory corruption. The V8 isolate held. The C++ glue around it did not.

So when n8n shipped an AI assistant that generates and executes code, the interesting part was never the assistant. It was the containment.

What's in the Box

Elestio now lists N8N-AI as a distinct catalog entry, and it's worth being precise about how it differs from the standard n8n deployment. It bundles:

Component Why it's there
n8n v2.35.7 The workflow editor itself, on port 5678.
AI Assistant + sandbox stack Generates code and runs it somewhere that isn't your n8n process.
Task runners (external mode) Moves Code node execution out of the main n8n process.
SearXNG Private web search, so research steps don't call a commercial search API.
PostgreSQL Real database instead of the SQLite default.
Redis Queue mode for parallel execution.

Two of those we've covered on their own: Redis queue mode and self-hosted SearXNG. The sandbox is the new thing, so that's where we'll spend the time.

How the Sandbox Actually Works

Upstream, the sandbox is not one container. It's four moving pieces:

  • sandbox-certs runs once, generates the TLS material, and exits. If you're watching docker compose ps and see it stopped, that's correct behaviour, not a crash.
  • sandbox-api is the control plane. n8n talks to it on port 8080 when a workflow wants to execute code.
  • sandbox-runner-1 is a privileged Docker-in-Docker container. sandbox-api hands it work over gRPC.
  • The ephemeral sandbox container is created per execution and thrown away afterwards.

Every hop between those services is mutual TLS, and the key files are locked to 0600. The chain is: n8n asks sandbox-api, sandbox-api delegates to the runner, the runner spawns a throwaway container, the container dies with the execution.

That's a meaningfully different posture from running untrusted code in-process. The blast radius of a bad generation is one container that was going to be destroyed anyway.

Why It Needs 2 vCPU and 4 GB

This is the practical consequence people miss when they size the VM like a normal n8n install.

sandbox-runner-1 is Docker-in-Docker. It runs its own container runtime inside a container, and every code execution spawns a fresh container inside that. Add PostgreSQL, Redis, SearXNG and n8n itself, and you're running six-plus containers where a basic n8n deployment runs one or two.

2 vCPU and 4 GB RAM is the floor, not a recommendation you can shave. Below that, the runner starts failing to spawn sandboxes and you get execution errors that look like application bugs but are memory pressure.

The Configuration That Bites

If you're building this yourself rather than deploying it managed, two details cost people the most time.

The secrets are not optional and not interchangeable. The .env needs distinct real values:

SANDBOX_API_KEYS=change-me-api-key
SANDBOX_API_RUNNER_REGISTRATION_TOKEN=change-me-registration-token
SANDBOX_API_RUNNER_API_KEY=change-me-runner-key
N8N_INSTANCE_AI_SANDBOX_API_KEY=change-me-api-key
SEARXNG_SECRET=change-me-searxng-secret
N8N_INSTANCE_AI_SEARXNG_URL=http://searxng:8080

Note that N8N_INSTANCE_AI_SANDBOX_API_KEY has to match a value in SANDBOX_API_KEYS. They're the two ends of the same handshake, and mismatching them produces an auth failure at execution time rather than at startup.

SearXNG will not answer n8n by default. It serves HTML, and n8n needs JSON. You have to enable the format explicitly in searxng-settings.yml:

use_default_settings: true
search:
  formats:
    - html
    - json

Miss that and web search silently returns nothing useful.

To turn on the assistant itself, add your model provider key:

N8N_INSTANCE_AI_MODEL_API_KEY=sk-ant-xxx

Three Security Rules

Only port 5678 faces the internet. Not 8080, and absolutely not the runner.

Never expose sandbox-runner-1. It's a privileged Docker-in-Docker container. Reaching it from outside is equivalent to root on the host.

The model API key never reaches the sandbox services. Upstream passes each sandbox service only the environment variables it needs, and the provider key isn't one of them. If you're writing your own compose file, don't undo that with a shared env_file.

Troubleshooting

sandbox-certs shows as exited. Working as designed. It's a one-shot job.

Code executions fail with auth errors. N8N_INSTANCE_AI_SANDBOX_API_KEY doesn't match an entry in SANDBOX_API_KEYS.

Web search returns empty results. JSON format isn't enabled in searxng-settings.yml.

Executions fail intermittently under load. Almost always the VM is undersized. Check memory before you check workflows.

Running on Windows. Keep the project folder inside the WSL filesystem. Under /mnt/c/ the Docker-in-Docker layer misbehaves.

Deploying It

Self-hosting this stack by hand is entirely doable, and the upstream Docker Compose guide is good. You're maintaining six services, mTLS certificates, and a privileged container, which is more surface than a typical n8n install.

If you'd rather skip that, N8N-AI on Elestio starts at $16/month fully managed, with SSL, automated backups, monitoring and updates handled, across Hetzner, DigitalOcean, Vultr, Linode, Scaleway, Netcup, AWS or your own VM. Size it at 2 vCPU / 4 GB minimum for the reasons above.

Either way, the thing worth keeping in mind is the one the isolated-vm advisory made concrete: the sandbox is not the feature you can afford to take on faith.

Thanks for reading ❤️ See you in the next one 👋