Skip to content

Commit d90a1fd

Browse files
authored
Merge pull request #116 from kossakovsky/feature/n8n-sandbox
feat: add n8n Assistant sandbox profile with Sysbox-isolated runner (1.11.0)
2 parents d608f52 + 527fa9c commit d90a1fd

15 files changed

Lines changed: 618 additions & 12 deletions

.env.example

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
N8N_ENCRYPTION_KEY=
1616
N8N_USER_MANAGEMENT_JWT_SECRET=
1717
N8N_RUNNERS_AUTH_TOKEN=
18+
N8N_SANDBOX_API_KEY=
19+
N8N_SANDBOX_RUNNER_API_KEY=
20+
N8N_SANDBOX_RUNNER_REGISTRATION_TOKEN=
1821

1922

2023
############
@@ -292,6 +295,42 @@ N8N_PAYLOAD_SIZE_MAX=256
292295
# Timezone for n8n and workflows (https://docs.n8n.io/hosting/configuration/environment-variables/timezone-localization/)
293296
GENERIC_TIMEZONE=America/New_York
294297

298+
############
299+
# [optional]
300+
# n8n Assistant sandbox (profile "n8n-sandbox")
301+
#
302+
# Runs the code the n8n AI Assistant / Agents generate (upstream
303+
# n8n-sandbox-service: sandbox-certs, sandbox-api, sandbox-runner-1). Select it
304+
# in the wizard; the model and its API key are set in the n8n UI under
305+
# Settings -> Instance AI. Internal only, there is no URL.
306+
#
307+
# Managed by the installer (05_configure_services.sh), do not edit by hand:
308+
# N8N_INSTANCE_AI_SANDBOX_ENABLED: true while the profile is active.
309+
# N8N_SANDBOX_RUNNER_RUNTIME / N8N_SANDBOX_RUNNER_PRIVILEGED: how the
310+
# Docker-in-Docker runner is isolated. "sysbox-runc" + false when the installer
311+
# could install Sysbox on this host; "runc" + true is the privileged fallback
312+
# you confirmed (root-equivalent on the host). "runc" + false is the shipped
313+
# default and means the profile is off - with the profile active the runner
314+
# cannot start ('make doctor' flags it). Re-run 'make update' to retry Sysbox.
315+
# N8N_INSTANCE_AI_SEARXNG_URL: set to http://searxng:8080 while the searxng
316+
# profile is active and cleared when it is not; a URL of your own is kept.
317+
#
318+
# Yours to tune:
319+
# N8N_ENABLED_MODULES: leave empty for n8n's defaults (instance-ai is on by
320+
# default). Set "instance-ai,agents" for the Agents preview. An unknown
321+
# module name stops n8n from booting.
322+
# N8N_SANDBOX_VERSION: tag of all three sandbox images (api, runner and the
323+
# sandbox image the runner pulls); they are released together and share one
324+
# tag. Set a release tag of ghcr.io/n8n-io/n8n-sandbox-service-api to pin
325+
# instead of ':latest'.
326+
############
327+
N8N_INSTANCE_AI_SANDBOX_ENABLED=false
328+
N8N_SANDBOX_RUNNER_RUNTIME=runc
329+
N8N_SANDBOX_RUNNER_PRIVILEGED=false
330+
N8N_INSTANCE_AI_SEARXNG_URL=
331+
N8N_ENABLED_MODULES=
332+
# N8N_SANDBOX_VERSION=
333+
295334
############
296335
# [optional]
297336
# n8n SMTP environment variables
@@ -579,7 +618,7 @@ GOST_UPSTREAM_PROXY=
579618

580619
# Internal services bypass list (prevents internal Docker traffic from going through proxy)
581620
# Includes: Docker internal networks (172.16-31.*, 10.*), Docker DNS (127.0.0.11), and all service hostnames
582-
GOST_NO_PROXY=localhost,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,.local,appsmith,postgres,postgres:5432,redis,redis:6379,caddy,ollama,ollama2,ollama3,ollama4,ollama5,ollama6,ollama7,ollama8,neo4j,qdrant,weaviate,clickhouse,minio,searxng,crawl4ai,gotenberg,langfuse-web,langfuse-worker,flowise,n8n,n8n-mcp,n8n-import,n8n-worker-1,n8n-worker-2,n8n-worker-3,n8n-worker-4,n8n-worker-5,n8n-worker-6,n8n-worker-7,n8n-worker-8,n8n-worker-9,n8n-worker-10,n8n-runner-1,n8n-runner-2,n8n-runner-3,n8n-runner-4,n8n-runner-5,n8n-runner-6,n8n-runner-7,n8n-runner-8,n8n-runner-9,n8n-runner-10,letta,lightrag,docling,postiz,temporal,temporal-ui,ragflow,ragflow-mysql,ragflow-minio,ragflow-redis,ragflow-elasticsearch,ragapp,open-webui,comfyui,invokeai,waha,libretranslate,paddleocr,nocodb,db,studio,kong,envoy,api-gw,supabase-envoy,auth,rest,realtime,storage,imgproxy,meta,functions,analytics,vector,supavisor,gost,uptime-kuma,python-runner,databasus,portainer,api.telegram.org,telegram.org,t.me,core.telegram.org
621+
GOST_NO_PROXY=localhost,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,.local,appsmith,postgres,postgres:5432,redis,redis:6379,caddy,ollama,ollama2,ollama3,ollama4,ollama5,ollama6,ollama7,ollama8,neo4j,qdrant,weaviate,clickhouse,minio,searxng,crawl4ai,gotenberg,langfuse-web,langfuse-worker,flowise,n8n,n8n-mcp,n8n-import,n8n-worker-1,n8n-worker-2,n8n-worker-3,n8n-worker-4,n8n-worker-5,n8n-worker-6,n8n-worker-7,n8n-worker-8,n8n-worker-9,n8n-worker-10,n8n-runner-1,n8n-runner-2,n8n-runner-3,n8n-runner-4,n8n-runner-5,n8n-runner-6,n8n-runner-7,n8n-runner-8,n8n-runner-9,n8n-runner-10,sandbox-certs,sandbox-api,sandbox-runner-1,letta,lightrag,docling,postiz,temporal,temporal-ui,ragflow,ragflow-mysql,ragflow-minio,ragflow-redis,ragflow-elasticsearch,ragapp,open-webui,comfyui,invokeai,waha,libretranslate,paddleocr,nocodb,db,studio,kong,envoy,api-gw,supabase-envoy,auth,rest,realtime,storage,imgproxy,meta,functions,analytics,vector,supavisor,gost,uptime-kuma,python-runner,databasus,portainer,api.telegram.org,telegram.org,t.me,core.telegram.org
583622

584623
############
585624
# Functions - Configuration for Functions

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
## [Unreleased]
44

5+
## [1.11.0] - 2026-09-09
6+
7+
### Added
8+
- **n8n Assistant sandbox** - New optional `n8n-sandbox` profile that adds n8n's own code-execution sandbox (`sandbox-certs`, `sandbox-api`, `sandbox-runner-1` from n8n-sandbox-service) so the built-in AI Assistant works on self-hosted n8n (and the Agents preview, except its knowledge base, which needs Daytona); until now the Instance AI settings page showed `Code sandbox: Not set`. The installer wires `N8N_INSTANCE_AI_SANDBOX_ENABLED`, the sandbox URL and key into n8n, generates the three shared secrets, and points `N8N_INSTANCE_AI_SEARXNG_URL` at the bundled SearXNG while that profile is active; the model API key is added in the n8n UI (Settings → Instance AI). The Docker-in-Docker runner is isolated with Sysbox: `scripts/setup_sysbox.sh` installs `sysbox-ce` non-interactively without restarting Docker (it pre-seeds `bip`/`default-address-pools` in `daemon.json` with Docker's current values) and the runner gets `runtime: sysbox-runc`. When Sysbox cannot be installed the installer asks before falling back to a privileged runner, records the choice in `N8N_SANDBOX_RUNNER_RUNTIME` / `N8N_SANDBOX_RUNNER_PRIVILEGED`, and `make doctor` warns while the runner is privileged. `N8N_ENABLED_MODULES` is exposed (empty by default) for the Agents preview. (#114)
9+
510
## [1.10.1] - 2026-09-02
611

712
### Fixed

CLAUDE.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ This is **Selfhost AI** (repository `selfhost-ai`, formerly `n8n-install`), a Do
4343
- `scripts/import_workflows.sh`: Imports workflows from `n8n/backup/workflows/` into n8n (used by `make import`)
4444
- `scripts/restart.sh`: Restarts services with proper compose file handling (used by `make restart`)
4545
- `scripts/setup_custom_tls.sh`: Configures custom TLS certificates (used by `make setup-tls`); supports `--remove` to revert to Let's Encrypt
46+
- `scripts/setup_sysbox.sh`: Installs Sysbox (`sysbox-runc`) for the n8n Assistant sandbox runner; called by `05_configure_services.sh` when the `n8n-sandbox` profile is active
4647
- `start_services.py`: Python orchestrator for service startup order, builds Docker images, handles external services (Supabase/Dify cloning, env preparation, startup), generates SearXNG secret key, stops existing containers. Uses `python-dotenv` (`dotenv_values`).
4748

4849
**Project Name**: All docker-compose commands use `-p localai` (defined in Makefile as `PROJECT_NAME := localai`).
@@ -184,6 +185,16 @@ This project uses [Semantic Versioning](https://semver.org/). When updating `CHA
184185
- No published ports and no Caddy block for extra instances - they are internal (`ollama2:11434`); `caddy-addon/site-*.conf` is the documented extension point
185186
- The generator is invoked unconditionally from `05_configure_services.sh`, which covers install and `make update`, and self-heals a stale file after a hardware-profile switch
186187

188+
### n8n Assistant sandbox (`n8n-sandbox` profile)
189+
190+
- Upstream n8n-sandbox-service as three services in `docker-compose.yml`: one-shot `sandbox-certs` (mTLS bootstrap into the `n8n_sandbox_tls` volume, skips when certs exist), `sandbox-api` (HTTP 8080 + gRPC 9090) and the Docker-in-Docker `sandbox-runner-1`. No ports, no Caddy block; n8n reaches `sandbox-api` by service name. **The cert SANs are the service names `sandbox-api` / `sandbox-runner-1` - do not rename them.** Exactly one runner by design.
191+
- All three images share `N8N_SANDBOX_VERSION` (default `latest`, like most images here). The runner pulls the sandbox image into its inner Docker on first use, and deliberately has **no volume** for that inner Docker: a persisted cache would keep an old sandbox image while `make update` moves api and runner forward. The cost is one ~330 MB pull after every recreate.
192+
- Three secrets, each referenced on both sides from one `.env` var: `N8N_SANDBOX_API_KEY` (n8n's `N8N_SANDBOX_SERVICE_API_KEY` = api's `SANDBOX_API_KEYS`), `N8N_SANDBOX_RUNNER_REGISTRATION_TOKEN`, `N8N_SANDBOX_RUNNER_API_KEY`.
193+
- The n8n env anchor carries `N8N_INSTANCE_AI_SANDBOX_ENABLED` (written `true`/`false` by `05_configure_services.sh` from the profile), the sandbox URL/key, `N8N_INSTANCE_AI_SEARXNG_URL` (set to `http://searxng:8080` while `searxng` is active, cleared otherwise; a custom value is left alone) and `N8N_ENABLED_MODULES` (**empty by default** - `instance-ai` is default-on in n8n, and an unknown module name stops n8n from booting, so never default it to a name). There is deliberately no `depends_on` from `n8n` to `sandbox-api`: a dependency on a profile-gated service breaks compose when the profile is off. The model/API key is configured in the n8n UI, not in compose.
194+
- Runner isolation: `runtime: "${N8N_SANDBOX_RUNNER_RUNTIME:-runc}"` + `privileged: "${N8N_SANDBOX_RUNNER_PRIVILEGED:-false}"` (compose casts the interpolated string to boolean). `05_configure_services.sh` runs `setup_sysbox.sh`; on success it writes `sysbox-runc`/`false`, otherwise it asks (`wt_yesno`, default No) before writing `runc`/`true`, and drops the profile on No. It also loads `br_netfilter` in both modes (the sandbox egress policy needs it) and persists it in `/etc/modules-load.d/n8n-sandbox.conf`. Without root, 05 exits 1 for this profile instead of configuring a runner that cannot start.
195+
- `setup_sysbox.sh` must stay non-interactive and must not restart Docker: the `sysbox-ce` package refuses to install while any container exists unless `/etc/docker/daemon.json` already has pretty-printed `bip` and `default-address-pools` keys, so the script pre-seeds them with Docker's current values via `jq --indent 4` and then the package only registers the runtime and SIGHUPs dockerd. Hosts with a custom Docker network setup (`-b`/`--bridge`/`--bip`/`--default-address-pool`/`--fixed-cidr` flags; `bridge`, `fixed-cidr`, `fixed-cidr-v6` or `ipv6: true` in daemon.json; no `docker0`) are rejected instead of guessed at. An already installed Sysbox is re-tested with a container on every run, never trusted from `dpkg` state alone. `jq` is not installed by 01/02 - only this script may rely on it, after its own apt step.
196+
- `make doctor` errors when `.env` says `sysbox-runc` but Docker lacks the runtime, and warns while the runner is privileged.
197+
187198
### Monitoring (Prometheus + Grafana)
188199

189200
- n8n metrics are enabled in the `x-n8n` anchor: `N8N_METRICS` plus `N8N_METRICS_INCLUDE_MESSAGE_EVENT_BUS_METRICS` / `_WORKFLOW_ID_LABEL` / `_WORKFLOW_NAME_LABEL` / `_WORKFLOW_INFO`. They expose `n8n_workflow_{started,success,failed,cancelled}_total{workflow_id,workflow_name}` and the `n8n_workflow_info` / `n8n_active_workflow_info` id-to-name gauges (leader main only). The alerts and recording rules also use `n8n_workflow_execution_duration_seconds{status,mode,workflow_id}`, which is on by default (`N8N_METRICS_INCLUDE_WORKFLOW_EXECUTION_DURATION`); turning it off silently disables them. Setting `N8N_METRICS_PREFIX` would break every panel and alert
@@ -230,6 +241,7 @@ Key functions:
230241
- `read_env_var "VAR_NAME"` / `write_env_var "VAR_NAME" "value"` - .env manipulation
231242
- `load_env` - Source .env file to make variables available
232243
- `update_compose_profiles "profile1,profile2"` - Update COMPOSE_PROFILES in .env
244+
- `remove_compose_profile "$list" "profile"` - Print a comma list without one profile (space-tolerant)
233245
- `gen_password 32` / `gen_hex 64` / `gen_base64 64` - Secret generation
234246
- `generate_bcrypt_hash "password"` - Create Caddy-compatible bcrypt hash (uses Caddy binary)
235247
- `json_escape "string"` - Escape string for JSON output
@@ -262,6 +274,7 @@ Common profiles:
262274
- `dify`: Dify AI platform (external compose, cloned at runtime; mutually exclusive with `supabase`)
263275
- `gost`: HTTP/HTTPS proxy for routing AI service outbound traffic
264276
- `python-runner`: Internal Python execution environment (no external access)
277+
- `n8n-sandbox`: n8n Assistant code-execution sandbox (requires `n8n`; internal only, see below)
265278
- `searxng`, `letta`, `lightrag`, `libretranslate`, `crawl4ai`, `docling`, `waha`, `comfyui`, `paddleocr`, `ragapp`, `gotenberg`, `postiz`, `n8n-mcp`: Additional optional services
266279

267280
## Architecture Patterns
@@ -412,6 +425,7 @@ bash -n scripts/install.sh
412425
bash -n scripts/restart.sh
413426
bash -n scripts/doctor.sh
414427
bash -n scripts/setup_custom_tls.sh
428+
bash -n scripts/setup_sysbox.sh
415429
bash -n scripts/docker_cleanup.sh
416430
```
417431

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ The installer also makes the following powerful open-source tools **available fo
6262

6363
[**n8n-MCP**](https://github.com/czlonkowski/n8n-mcp) - A Model Context Protocol server that gives AI coding assistants (Claude Code, Cursor, Windsurf, VS Code Copilot) indexed access to every n8n node's documentation, property schemas and thousands of workflow templates - and, once you add an n8n API key, the ability to create and update workflows in your n8n instance straight from your IDE.
6464

65+
[**n8n Assistant sandbox**](https://docs.n8n.io/deploy/host-n8n/configure-n8n/set-up-n8n-assistant) - Code-execution sandbox for n8n's built-in AI Assistant and the Agents preview (n8n's own sandbox service, Docker-in-Docker isolated with Sysbox; internal only, see [below](#n8n-assistant-sandbox-ai-assistant--agents)).
66+
6567
[**ComfyUI**](https://github.com/comfyanonymous/ComfyUI) - A powerful, node-based UI for Stable Diffusion workflows. Build and run image-generation pipelines visually, with support for custom nodes and extensions.
6668

6769
[**Crawl4ai**](https://github.com/unclecode/crawl4ai) - A flexible web crawler designed for AI, enabling you to extract data from websites for your projects.
@@ -278,6 +280,21 @@ The custom n8n Docker image (`n8n/Dockerfile.n8n`) includes the following system
278280
- Resizing or compressing media files
279281
- Generating thumbnails from videos
280282
283+
### n8n Assistant sandbox (AI Assistant / Agents)
284+
285+
n8n's built-in AI Assistant and the Agents preview build, edit and debug workflows through conversation, and on a self-hosted instance they need a code-execution sandbox before they do anything (the Instance AI settings page shows `Code sandbox: Not set` until one exists). Select **n8n Assistant sandbox** in the wizard (`n8n-sandbox` profile, requires `n8n`) and the installer adds n8n's own sandbox stack next to n8n: a one-shot certificate job, the `sandbox-api` service and a Docker-in-Docker `sandbox-runner-1`. Nothing is published; n8n talks to `sandbox-api` over the internal network. Budget about 4 GB of extra RAM for it.
286+
287+
After installation, open n8n → **Settings → Instance AI** and add a model API key (Anthropic, OpenAI, OpenRouter or any OpenAI-compatible endpoint). The sandbox and, when the `searxng` profile is active, web search through the bundled SearXNG are already wired in. n8n's docs position this bundled sandbox as the self-hosted option and Daytona as the hosted one; the Agents knowledge base is the only feature that needs Daytona.
288+
289+
**How the runner is isolated.** Docker-in-Docker normally requires a `privileged` container, which is root-equivalent on the host. The installer avoids that by installing [Sysbox](https://github.com/nestybox/sysbox) (`scripts/setup_sysbox.sh`) and running the runner with `runtime: sysbox-runc`. This installer supports Sysbox on Ubuntu 20.04/22.04/24.04 or Debian 11 (amd64/arm64) and, following n8n's sandbox service, requires a kernel newer than 5.19, Docker installed natively (Sysbox does not support the snap), `/var/lib` and Docker's data root on ext4 or btrfs, and a VM or bare-metal host (not an LXC or other system container). The installer also loads the `br_netfilter` module (the sandbox network policy needs it in both isolation modes) and, before installing Sysbox, adds `bip` and `default-address-pools` entries to `/etc/docker/daemon.json` that mirror Docker's current values: the Sysbox package needs them present to install without rewriting Docker's networking or restarting Docker, so running services are not interrupted. If a prerequisite is missing, or your Docker network setup is custom (`-b`/`--bridge`, `--bip`, `--fixed-cidr` or `--default-address-pool` flags; `bridge`, `fixed-cidr`, `fixed-cidr-v6` or `ipv6: true` in `daemon.json`; no `docker0` bridge), the installer asks whether to run the runner **privileged** instead; say No and the profile is dropped. The choice is recorded in `.env` as `N8N_SANDBOX_RUNNER_RUNTIME` / `N8N_SANDBOX_RUNNER_PRIVILEGED`, `make doctor` warns while the runner is privileged, and `make update` retries Sysbox. On a host you configured yourself, install the `sysbox-ce` package by hand ([release 0.7.1](https://github.com/nestybox/sysbox/releases/tag/v0.7.1); its installer registers the runtime, but refuses to run while containers exist unless `daemon.json` already has `bip` and `default-address-pools`, so do it with the stack stopped) and run `make update`.
290+
291+
Notes:
292+
293+
- **Agents preview**: set `N8N_ENABLED_MODULES=instance-ai,agents` in `.env` and `make restart`. Leave it empty otherwise; an unknown module name stops n8n from booting. The Agents knowledge base additionally needs a Daytona sandbox, which this stack does not provide.
294+
- **Versions**: the three sandbox images are released together and follow `:latest`; set `N8N_SANDBOX_VERSION` in `.env` to pin one release for all of them. The runner downloads the sandbox image (about 330 MB) on first use after each `make update` or `make restart`, so the first assistant request after that takes a little longer.
295+
- **Certificates** between `sandbox-api` and the runner are generated once into the `localai_n8n_sandbox_tls` volume and do not renew themselves. To regenerate: `docker compose -p localai rm -sf sandbox-certs sandbox-api sandbox-runner-1`, then `docker volume rm localai_n8n_sandbox_tls`, then `make restart`.
296+
- **Removing Sysbox**: `sudo apt-get purge sysbox-ce && sudo userdel sysbox`; the two `daemon.json` entries and `/etc/modules-load.d/n8n-sandbox.conf` (br_netfilter autoload) are harmless and can stay.
297+
281298
## Upgrading
282299

283300
To update all components (n8n, Open WebUI, etc.) to their latest versions and incorporate the newest changes from this installer project:

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.10.1
1+
1.11.0

0 commit comments

Comments
 (0)