Skip to content

feat: add opt-in AI Assistant (instance-ai) sandbox to Docker Compose examples - #170

Open
krider2010 wants to merge 5 commits into
mainfrom
krider2010/align-docker-compose-docs
Open

feat: add opt-in AI Assistant (instance-ai) sandbox to Docker Compose examples#170
krider2010 wants to merge 5 commits into
mainfrom
krider2010/align-docker-compose-docs

Conversation

@krider2010

@krider2010 krider2010 commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

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, and docker-caddy — gain the three sandbox services (sandbox-certs, sandbox-api, and the privileged Docker-in-Docker sandbox-runner-1).

Rather than a separate example folder, it's wired into each existing one as an opt-in ai-assistant Compose profile, since the Assistant is becoming a core part of n8n.

How it behaves

  • Default docker compose up -d is unchanged — no privileged container, no required AI keys. The sandbox services only start with --profile ai-assistant.
  • n8n's instance-ai env vars default to empty, so the module only loads once you opt in — no crash-loop or privileged DinD by default, and no hard depends_on that would block the base stack.
  • To enable: uncomment the AI Assistant block in that folder's .env (model API key + sandbox secrets) and run docker compose --profile ai-assistant up -d.
  • In the worker example the module is applied to the main n8n only (via env_file), not the workers/runners.
  • Sandbox images use ${SANDBOX_VERSION:-latest} so they can be pinned later; n8n stays on ${N8N_VERSION}.
  • Each README gets an "AI Assistant (optional)" section with a security checklist (privileged runner ≈ root, never expose 8080/9090, rotate secrets, regenerate the sandbox-tls volume).

Testing

Verified end-to-end against OrbStack:

  • Default path: withPostgres up with no profile → only postgres/n8n/n8n-runner, /healthz OK, no module loaded, no privileged container.
  • Profile path (from the committed files): certs bootstrap and exit, sandbox-api goes healthy, and the runner registers (api → "runner registered, runner_id: runner-1", heartbeat sent, sandbox image ready).
  • docker compose config on all four confirms the profile gating (default excludes the sandbox / renders no instance-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 the ai-assistant profile. Defaults are unchanged; enabling the profile starts internal sandbox services with tighter secret handling, stricter mTLS permissions, and warning‑free defaults.

  • New Features

    • Added sandbox-certs, sandbox-api, and privileged sandbox-runner-1, gated by the ai-assistant profile; services are internal-only and use ${SANDBOX_VERSION:-latest} for pinning.
    • Instance config stays opt-in: N8N_ENABLED_MODULES and N8N_INSTANCE_AI_* default to empty; in the worker example only the main n8n loads the module via env_file.
  • Bug Fixes

    • Stopped secret leakage by removing env_file: .env from sandbox-api and sandbox-runner-1; only SANDBOX_API_* are passed.
    • Kept mTLS keys private: sandbox-api owns /tls/api; ca.key stays root-only.
    • Silenced default-path warnings by giving SANDBOX_* interpolations empty defaults (:-) so runs without the profile are clean.

Written for commit 8c9cd0d. Summary will update on new commits.

Review in cubic

… 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>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Loading

Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

Comment thread docker-compose/withPostgres/docker-compose.yml Outdated
Comment thread docker-compose/subfolderWithSSL/docker-compose.yml
Comment thread docker-compose/subfolderWithSSL/docker-compose.yml Outdated
# 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

@cubic-dev-ai cubic-dev-ai Bot Jul 16, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Fix with cubic

- 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:-}

@cubic-dev-ai cubic-dev-ai Bot Jul 16, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Fix with cubic

krider2010 and others added 2 commits July 17, 2026 12:33
…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>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread docker-compose/withPostgres/docker-compose.yml Outdated
…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>
@krider2010
krider2010 force-pushed the krider2010/align-docker-compose-docs branch from 0f0bcf8 to 3099475 Compare July 17, 2026 11:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant