feat: add opt-in AI Assistant (instance-ai) sandbox to Docker Compose examples - #170
feat: add opt-in AI Assistant (instance-ai) sandbox to Docker Compose examples#170krider2010 wants to merge 5 commits into
Conversation
… examples
Adds n8n's AI Assistant sandbox (sandbox-certs, sandbox-api, and the
privileged Docker-in-Docker sandbox-runner-1) to all four Docker Compose
examples behind an opt-in `ai-assistant` Compose profile. A plain
`docker compose up -d` is unchanged; enable with
`docker compose --profile ai-assistant up -d` after filling in the
AI Assistant block in .env.
- n8n's instance-ai env vars default to empty so the module only loads
when opted in (no crash-loop / privileged container by default).
- In the worker example the module is applied to the main n8n only, not
workers/runners, via env_file.
- Sandbox images use ${SANDBOX_VERSION:-latest} (pinnable); n8n stays on
${N8N_VERSION}.
- Each README gains an "AI Assistant (optional)" section with a security
checklist (privileged DinD, never expose 8080/9090, rotate secrets,
regenerate the sandbox-tls volume).
Verified end-to-end against OrbStack: default path unaffected; with the
profile the certs bootstrap, sandbox-api goes healthy, and the runner
registers with the API.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
2 issues found across 13 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="docker-compose/withPostgresAndWorker/docker-compose.yml">
<violation number="1" location="docker-compose/withPostgresAndWorker/docker-compose.yml:74">
P2: After AI settings are configured once, a normal `docker compose up -d` still enables `instance-ai` on the unprofiled `n8n` service while the profile-gated sandbox remains down. This breaks the stated opt-in/profile behavior; use a profile-specific override or otherwise make module settings conditional on the profile.</violation>
</file>
<file name="docker-compose/withPostgres/docker-compose.yml">
<violation number="1" location="docker-compose/withPostgres/docker-compose.yml:42">
P2: Starting without `--profile ai-assistant` after configuring `.env` loads `instance-ai` in n8n but leaves its required sandbox services absent, so Assistant requests target an unresolvable `sandbox-api`. Put AI-specific n8n environment in a profile-specific override/config path so module activation and sandbox startup are gated together.</violation>
</file>
Architecture diagram
sequenceDiagram
participant User as Operator
participant Compose as Docker Compose
participant Certs as sandbox-certs
participant TLS as sandbox-tls Volume
participant API as sandbox-api
participant Runner as sandbox-runner-1
participant n8n as n8n Container
participant Model as External Model API
participant Internet as Public Network
Note over User,Internet: NEW: AI Assistant sandbox startup flow (opt-in via --profile ai-assistant)
alt Default: no profile (docker compose up -d)
User->>Compose: start without --profile ai-assistant
Compose->>n8n: start n8n, Postgres, workers only
Note over n8n: N8N_INSTANCE_AI_SANDBOX_ENABLED=false<br/>No instance-ai module loaded
Note over Compose: sandbox-certs, sandbox-api, sandbox-runner-1 NOT started
else Opt-in: --profile ai-assistant
User->>Compose: docker compose --profile ai-assistant up -d
Compose->>Certs: start sandbox-certs (profiles match)
Certs->>Certs: bootstrap-mtls.sh --out-dir /tls --api-san sandbox-api --control-san-prefix sandbox-runner
Certs->>TLS: generate CA + server/client certs for API and runner
Certs->>TLS: chown/chmod for sandbox-api user
Certs-->>Compose: exits (service_completed_successfully)
Note over Certs: Run-once certificate generation
Compose->>API: start sandbox-api (depends_on sandbox-certs completed)
API->>TLS: read /tls/api/*.crt and *.key (read-only)
API->>API: listen on gRPC :9090 and HTTP :8080
API->>API: healthcheck: wget -qO- http://localhost:8080/healthz
API-->>Compose: healthy
Compose->>Runner: start sandbox-runner-1 (depends_on sandbox-api healthy)
Runner->>TLS: read /tls/runner/*.crt and *.key (read-only)
Runner->>API: Registration gRPC (mTLS)<br/>SANDBOX_RUNNER_REGISTRATION_TOKEN + API keys
API-->>Runner: registration acknowledged, runner registered (id: runner-1)
Note over Runner,API: Heartbeat / runner control via gRPC :9091
Runner->>Runner: start Docker-in-Docker daemon (privileged)
Runner->>API: heartbeat: runner ready, sandbox image available
Compose->>n8n: start n8n (no depends_on on sandbox services, env vars loaded from .env)
n8n->>n8n: load instance-ai module because N8N_ENABLED_MODULES set
n8n->>API: HTTP to sandbox-api:8080 using N8N_INSTANCE_AI_SANDBOX_API_KEY
API-->>n8n: accept, forward code execution request
n8n->>Model: call model API (e.g., OpenAI) with N8N_INSTANCE_AI_MODEL_API_KEY
Model-->>n8n: response
n8n->>API: request sandbox execution of generated code
API->>Runner: gRPC control: deploy sandbox container for execution
Runner->>Runner: spin-up sandbox container (Docker-in-Docker)<br/>using n8nio/n8n-sandbox-service-sandbox
Runner-->>API: execution result
API-->>n8n: result
n8n-->>User: display AI Assistant output
Note over Internet: Security boundary: sandbox ports not exposed to host/public
Note over Runner: PRIVILEGED DinD – treat as root on host
end
Tip: instead of fixing issues one by one fix them all with cubic
Re-trigger cubic
| # AI Assistant (instance-ai): env_file injects the optional N8N_ENABLED_MODULES / N8N_INSTANCE_AI_* | ||
| # values from .env into the main instance only — workers and runners must NOT load the module. | ||
| # They stay commented in .env by default; enable with `docker compose --profile ai-assistant up -d`. | ||
| env_file: .env |
There was a problem hiding this comment.
P2: After AI settings are configured once, a normal docker compose up -d still enables instance-ai on the unprofiled n8n service while the profile-gated sandbox remains down. This breaks the stated opt-in/profile behavior; use a profile-specific override or otherwise make module settings conditional on the profile.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docker-compose/withPostgresAndWorker/docker-compose.yml, line 74:
<comment>After AI settings are configured once, a normal `docker compose up -d` still enables `instance-ai` on the unprofiled `n8n` service while the profile-gated sandbox remains down. This breaks the stated opt-in/profile behavior; use a profile-specific override or otherwise make module settings conditional on the profile.</comment>
<file context>
@@ -67,6 +68,10 @@ services:
+ # AI Assistant (instance-ai): env_file injects the optional N8N_ENABLED_MODULES / N8N_INSTANCE_AI_*
+ # values from .env into the main instance only — workers and runners must NOT load the module.
+ # They stay commented in .env by default; enable with `docker compose --profile ai-assistant up -d`.
+ env_file: .env
ports:
- 5678:5678
</file context>
| - N8N_RUNNERS_BROKER_LISTEN_ADDRESS=0.0.0.0 | ||
| # AI Assistant (instance-ai). Empty by default; set these in .env and start with | ||
| # `docker compose --profile ai-assistant up -d` to enable. See the README. | ||
| - N8N_ENABLED_MODULES=${N8N_ENABLED_MODULES:-} |
There was a problem hiding this comment.
P2: Starting without --profile ai-assistant after configuring .env loads instance-ai in n8n but leaves its required sandbox services absent, so Assistant requests target an unresolvable sandbox-api. Put AI-specific n8n environment in a profile-specific override/config path so module activation and sandbox startup are gated together.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docker-compose/withPostgres/docker-compose.yml, line 42:
<comment>Starting without `--profile ai-assistant` after configuring `.env` loads `instance-ai` in n8n but leaves its required sandbox services absent, so Assistant requests target an unresolvable `sandbox-api`. Put AI-specific n8n environment in a profile-specific override/config path so module activation and sandbox startup are gated together.</comment>
<file context>
@@ -36,6 +37,15 @@ services:
- N8N_RUNNERS_BROKER_LISTEN_ADDRESS=0.0.0.0
+ # AI Assistant (instance-ai). Empty by default; set these in .env and start with
+ # `docker compose --profile ai-assistant up -d` to enable. See the README.
+ - N8N_ENABLED_MODULES=${N8N_ENABLED_MODULES:-}
+ - N8N_INSTANCE_AI_MODEL=${N8N_INSTANCE_AI_MODEL:-}
+ - N8N_INSTANCE_AI_SANDBOX_ENABLED=${N8N_INSTANCE_AI_SANDBOX_ENABLED:-false}
</file context>
…stant docs Addresses code-review feedback on the AI Assistant sandbox: - Drop `env_file: .env` from sandbox-api and sandbox-runner-1. env_file injected the whole .env (model/search API keys, DB password, n8n encryption key) into the privileged runner, which needs none of it. The runner's config is already fully explicit; sandbox-api now gets only the three SANDBOX_API_* secrets it needs, passed by interpolation. - Note in each .env that the privileged runner should be pinned to a reviewed release rather than the mutable `latest` tag. - Note in each README that the `.env` values and `--profile ai-assistant` must be enabled together, so the module isn't loaded while the sandbox is down. Verified end-to-end against OrbStack: runner still registers, and the model API key / DB password / encryption key are no longer present in the sandbox-api or sandbox-runner-1 environments. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The sandbox-certs bootstrap used `--world-readable` plus `chmod -R a+rX /tls`, which made every private key — including the root CA signing key — readable by any user inside the sandbox-api and (privileged) sandbox-runner containers. Drop both. sandbox-api runs as the non-root `sandbox-api` user, so chown-ing /tls/api to it lets it read its own leaf keys at 0600; the runner runs as root and reads /tls/runner at 0600 with no widening; ca.key stays root-only 0600 (nothing needs it at runtime). Verified against OrbStack: api healthy, runner registers, ca.key no longer readable by the sandbox-api user, leaf keys are 0600. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
All reported issues were addressed across 12 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
…nings
The explicit SANDBOX_* interpolations on the profiled sandbox services used
${VAR} without a default. Compose interpolates profiled services before
profile selection, so a plain `docker compose up -d` (AI Assistant not
enabled) emitted "variable is not set" warnings for the sandbox secrets.
Add `:-` empty defaults, matching the n8n instance-ai vars, so the default
path is warning-free. No effect when enabled (.env supplies the values) and
the services' own "must be set" checks still guard the enabled path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
0f0bcf8 to
3099475
Compare
What
Brings the Docker Compose examples in line with the new "Deploy n8n with Docker Compose" docs, which now cover the AI Assistant (
instance-ai) sandbox. All four examples —withPostgres,withPostgresAndWorker,subfolderWithSSL, anddocker-caddy— gain the three sandbox services (sandbox-certs,sandbox-api, and the privileged Docker-in-Dockersandbox-runner-1).Rather than a separate example folder, it's wired into each existing one as an opt-in
ai-assistantCompose profile, since the Assistant is becoming a core part of n8n.How it behaves
docker compose up -dis unchanged — no privileged container, no required AI keys. The sandbox services only start with--profile ai-assistant.instance-aienv vars default to empty, so the module only loads once you opt in — no crash-loop or privileged DinD by default, and no harddepends_onthat would block the base stack..env(model API key + sandbox secrets) and rundocker compose --profile ai-assistant up -d.env_file), not the workers/runners.${SANDBOX_VERSION:-latest}so they can be pinned later; n8n stays on${N8N_VERSION}.sandbox-tlsvolume).Testing
Verified end-to-end against OrbStack:
withPostgresup with no profile → only postgres/n8n/n8n-runner,/healthzOK, no module loaded, no privileged container.sandbox-apigoes healthy, and the runner registers (api → "runner registered, runner_id: runner-1", heartbeat sent, sandbox image ready).docker compose configon all four confirms the profile gating (default excludes the sandbox / renders noinstance-ai).Not verified: driving the Assistant to actually generate + execute code, which needs a real model API key. The full infra path (certs → api → runner registration → n8n reaching the api with the module loaded) is confirmed.
🤖 Generated with Claude Code
Summary by cubic
Adds an opt-in AI Assistant (
instance-ai) sandbox to all Docker Compose examples behind theai-assistantprofile. Defaults are unchanged; enabling the profile starts internal sandbox services with tighter secret handling, stricter mTLS permissions, and warning‑free defaults.New Features
sandbox-certs,sandbox-api, and privilegedsandbox-runner-1, gated by theai-assistantprofile; services are internal-only and use${SANDBOX_VERSION:-latest}for pinning.N8N_ENABLED_MODULESandN8N_INSTANCE_AI_*default to empty; in the worker example only the mainn8nloads the module viaenv_file.Bug Fixes
env_file: .envfromsandbox-apiandsandbox-runner-1; onlySANDBOX_API_*are passed.sandbox-apiowns/tls/api;ca.keystays root-only.SANDBOX_*interpolations empty defaults (:-) so runs without the profile are clean.Written for commit 8c9cd0d. Summary will update on new commits.