Author SHA1 Message Date
git-hermes 7a58959869 fix(n8n-sandbox): reset certificate readiness marker
Test / test (pull_request) Canceled after 0s
2026-09-03 10:31:24 +02:00
git-hermes 0a5476793e fix(n8n-sandbox): keep Runtipi app status healthy
Test / test (pull_request) Canceled after 0s
2026-09-03 10:28:42 +02:00
git-hermes 389d4e5b13 docs(n8n-sandbox): clarify post-install setup (#4)
Test / test (push) Canceled after 0s
Document the actual n8n override and optional SearXNG JSON configuration.
2026-09-03 08:06:39 +00:00
git-hermes bb8fdc64ba docs(n8n-sandbox): clarify post-install setup
Test / test (pull_request) Canceled after 0s
2026-09-03 10:03:59 +02:00
4 changed files with 103 additions and 26 deletions
+26 -3
View File
@@ -78,15 +78,38 @@ describe("n8n-sandbox installation secrets", () => {
})
})
describe("n8n-sandbox post-install documentation", () => {
test("documents the actual n8n override and optional SearXNG JSON setup", async () => {
const description = await getFile('n8n-sandbox', 'metadata/description.md')
expect(description).toContain('services:\n n8n-2:\n environment:')
expect(description).toContain('N8N_SANDBOX_SERVICE_URL=http://sandbox-api:8080')
expect(description).not.toContain('sandbox-api:<PORT')
expect(description).toContain('N8N_SANDBOX_SERVICE_API_KEY=<clé choisie lors de linstallation>')
expect(description).toContain('/opt/runtipi/app-data/migrated/searxng/data/settings.yml')
expect(description).toContain('- json')
})
})
describe("modern compose files preserve runtime semantics", () => {
test("n8n-sandbox keeps its one-shot certificate service", async () => {
test("n8n-sandbox keeps its certificate bootstrap healthy for Runtipi", async () => {
const fileContent = await getFile('n8n-sandbox', 'docker-compose.yml')
expect(fileContent).not.toBeNull()
const parsed = YAML.parse(fileContent || '')
const certs = parsed.services?.['sandbox-certs']
const api = parsed.services?.['sandbox-api']
expect(parsed['x-runtipi']?.schema_version).toBe(2)
expect(parsed.services?.['sandbox-certs']?.restart).toBe('no')
expect(parsed.services?.['sandbox-api']?.['x-runtipi']?.is_main).toBe(true)
const certCommand = certs?.command?.join(' ') || ''
expect(certs?.restart).toBe('unless-stopped')
expect(certCommand).toContain('rm -f /tmp/certs-ready')
expect(certCommand).toContain('touch /tmp/certs-ready')
expect(certCommand).toContain('tail -f /dev/null')
expect(certCommand.indexOf('rm -f /tmp/certs-ready')).toBeLessThan(certCommand.indexOf('bootstrap-mtls.sh'))
expect(certs?.healthcheck?.test).toContain('test -f /tmp/certs-ready')
expect(api?.depends_on?.['sandbox-certs']?.condition).toBe('service_healthy')
expect(api?.['x-runtipi']?.is_main).toBe(true)
expect(parsed.services?.['sandbox-runner-1']?.environment?.SANDBOX_RUNNER_HTTP_BASE_URL).toBe('https://sandbox-runner-1:8080')
expect(parsed.services?.['sandbox-runner-1']?.healthcheck?.test).toContain('https://localhost:8080/readyz')
})
+1 -1
View File
@@ -6,7 +6,7 @@
"no_gui": true,
"dynamic_config": true,
"port": 8080,
"tipi_version": 2,
"tipi_version": 4,
"min_tipi_version": "4.7.0",
"version": "1.3.0",
"author": "n8n",
+16 -8
View File
@@ -32,24 +32,32 @@
services:
sandbox-certs:
image: n8nio/n8n-sandbox-service-api:1.3.0
# Obligatoire sous Runtipi : sans cette ligne le builder injecte
# restart: unless-stopped et ce job one-shot boucle en redemarrage.
restart: "no"
# Runtipi considere l'app arretee si un seul de ses conteneurs est sorti.
# Le bootstrap reste donc actif et sain apres la generation idempotente.
restart: unless-stopped
user: "0:0"
entrypoint: ["sh", "-c"]
# Genere les certs (idempotent) puis fixe les droits pour l'utilisateur
# sandbox-api de l'image : /tls/api et le repertoire SQLite de l'API
# (en bind mount, Docker cree le dossier en root sinon).
# Genere les certs, fixe les droits pour sandbox-api, publie l'etat ready,
# puis reste actif sans ouvrir de port ni lancer de service reseau.
command:
- >
rm -f /tmp/certs-ready &&
bootstrap-mtls.sh --out-dir /tls --api-san sandbox-api
--control-san-prefix sandbox-runner &&
chown -R sandbox-api:sandbox-api /tls/api /var/lib/n8n-sandbox-api
chown -R sandbox-api:sandbox-api /tls/api /var/lib/n8n-sandbox-api &&
touch /tmp/certs-ready &&
exec tail -f /dev/null
environment:
NUM_RUNNERS: "1"
volumes:
- ${APP_DATA_DIR}/data/tls:/tls
- ${APP_DATA_DIR}/data/api:/var/lib/n8n-sandbox-api
healthcheck:
test: "test -f /tmp/certs-ready"
interval: 5s
timeout: 2s
retries: 12
start_period: 5s
sandbox-api:
image: n8nio/n8n-sandbox-service-api:1.3.0
@@ -57,7 +65,7 @@ services:
depends_on:
sandbox-certs:
condition: service_completed_successfully
condition: service_healthy
environment:
SANDBOX_API_KEYS: "${SANDBOX_API_KEYS}"
+60 -14
View File
@@ -1,33 +1,79 @@
# n8n Sandbox Service
Sandbox auto-hébergé pour l'**AI Assistant** de n8n (setup « Self-host the sandbox manually » de la doc n8n). L'app déploie les trois services de la stack officielle :
Sandbox auto-hébergé pour l'**AI Assistant** de n8n (configuration « Self-host the sandbox manually » de la documentation n8n). L'app déploie les trois services de la stack officielle :
| Service | Rôle |
|---|---|
| `sandbox-certs` | Job one-shot : génère la CA privée et les certificats mTLS, puis s'arrête. |
| `sandbox-api` | Point d'entrée HTTP (`:8080`) que n8n appelle pour exécuter du code. |
| `sandbox-certs` | Initialise la CA privée et les certificats mTLS, puis reste inactif et sain pour que Runtipi conserve l'app au statut « Démarré ». |
| `sandbox-api` | Point d'entrée HTTP interne (`:8080`) que n8n appelle pour exécuter du code. |
| `sandbox-runner-1` | Docker-in-Docker **privileged** : crée et exécute les conteneurs sandbox. |
## Après l'installation
Lors de l'installation, choisir une clé forte dans le champ **Clé API partagée avec n8n**. Dans l'app officielle **n8n** (paramètres ou `app.env`), ajouter le bloc suivant en recopiant cette même clé :
Pendant l'installation, choisir une clé forte dans le champ **Clé API partagée avec n8n** et la conserver dans un gestionnaire de mots de passe.
```
N8N_ENABLED_MODULES=instance-ai
N8N_INSTANCE_AI_SANDBOX_ENABLED=true
N8N_INSTANCE_AI_SANDBOX_PROVIDER=n8n-sandbox
N8N_INSTANCE_AI_SANDBOX_IMAGE=n8nio/n8n-sandbox-service-sandbox:1.3.0
N8N_SANDBOX_SERVICE_URL=http://sandbox-api:8080
N8N_SANDBOX_SERVICE_API_KEY=<même valeur que « Clé API partagée avec n8n »>
N8N_PROXY_HOPS=1
Dans l'application officielle **n8n**, activer la **configuration utilisateur Docker Compose**, puis ajouter :
```yaml
# Add your docker-compose overrides here.
# The overrides will be merged with the generated docker-compose.yml file.
# Heure de Paris
services:
n8n-2:
environment:
- GENERIC_TIMEZONE=Europe/Paris
# AI Assistant et Sandbox externe
- N8N_ENABLED_MODULES=instance-ai
- N8N_INSTANCE_AI_SANDBOX_ENABLED=true
- N8N_INSTANCE_AI_SANDBOX_PROVIDER=n8n-sandbox
- N8N_INSTANCE_AI_SANDBOX_IMAGE=n8nio/n8n-sandbox-service-sandbox:1.3.0
- N8N_SANDBOX_SERVICE_URL=http://sandbox-api:8080
- N8N_SANDBOX_SERVICE_API_KEY=<clé choisie lors de linstallation>
```
Puis redémarrer n8n et vérifier depuis son conteneur :
Remplacer entièrement `<clé choisie lors de linstallation>` par la vraie clé, sans conserver les caractères `<` et `>`. Ne jamais publier cette valeur.
Le port de `N8N_SANDBOX_SERVICE_URL` reste `8080` : il s'agit du port interne du service Docker, pas du port éventuellement choisi dans l'interface Runtipi.
Enregistrer la configuration, puis redémarrer l'application **n8n**. Pour vérifier la communication depuis son conteneur :
```sh
wget -qO- http://sandbox-api:8080/healthz
```
wget -qO- http://sandbox-api:8080/healthz # {"status":"ok"}
La réponse attendue est `{"status":"ok"}`.
## Recherche web avec SearXNG (facultatif)
SearXNG est une application séparée et n'est pas nécessaire au fonctionnement du sandbox. L'installer seulement si les workflows ou outils IA de n8n doivent effectuer des recherches web.
Pour autoriser les réponses JSON de SearXNG, modifier :
```sh
sudo nano /opt/runtipi/app-data/migrated/searxng/data/settings.yml
```
Conserver les autres réglages existants et vérifier que le fichier contient :
```yaml
use_default_settings: true
search:
formats:
- html
- json
```
Contrôle facultatif du contenu et des fins de ligne :
```sh
sudo cat -A /opt/runtipi/app-data/migrated/searxng/data/settings.yml
```
Redémarrer ensuite l'application **SearXNG**, puis redémarrer **n8n** si sa configuration a également été modifiée.
## Données persistantes
Tout est sous `app-data/<store>/n8n-sandbox/data/` :