add start_services.py to key files, document python task runner, docker-compose.override.yml support, yaml anchors, restart behavior, supabase/dify profiles, --update flag for secrets, and expand file locations and syntax validation lists
21 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
This is n8n-install, a Docker Compose-based installer that provides a comprehensive self-hosted environment for n8n workflow automation and numerous AI/automation services. The installer includes an interactive wizard, automated secret generation, and integrated HTTPS via Caddy.
Core Architecture
- Profile-based service management: Services are activated via Docker Compose profiles (e.g.,
n8n,flowise,monitoring). Profiles are stored in the.envfile'sCOMPOSE_PROFILESvariable. - No exposed ports: Services do NOT publish ports directly. All external HTTPS access is routed through Caddy reverse proxy on ports 80/443.
- Shared secrets: Core services (Postgres, Valkey (Redis-compatible, container named
redisfor backward compatibility), Caddy) are always included. Other services are optional and selected during installation. - Queue-based n8n: n8n runs in
queuemode with Redis, Postgres, and dynamically scaled workers (N8N_WORKER_COUNT).
Key Files
Makefile: Common commands (install, update, logs, etc.)docker-compose.yml: Service definitions with profilesCaddyfile: Reverse proxy configuration with automatic HTTPS.env: Generated secrets and configuration (from.env.example)scripts/install.sh: Main installation orchestrator (runs numbered scripts 01-08 in sequence)scripts/utils.sh: Shared utility functions (sourced by all scripts viasource "$(dirname "$0")/utils.sh" && init_paths)scripts/01_system_preparation.sh: System updates, firewall, security hardeningscripts/02_install_docker.sh: Docker and Docker Compose installationscripts/git.sh: Git utilities (sync with origin, branch detection, configuration)scripts/03_generate_secrets.sh: Secret generation and bcrypt hashingscripts/04_wizard.sh: Interactive service selection using whiptailscripts/05_configure_services.sh: Service-specific configuration logicscripts/databases.sh: Creates isolated PostgreSQL databases for services (library)scripts/telemetry.sh: Anonymous telemetry functions (Scarf integration)scripts/06_run_services.sh: Starts Docker Compose stackscripts/07_final_report.sh: Post-install credential summaryscripts/08_fix_permissions.sh: Fixes file ownership for non-root accessscripts/generate_n8n_workers.sh: Generates dynamic worker/runner compose filescripts/update.sh: Update orchestrator (syncs with origin and updates images)scripts/update_preview.sh: Preview available updates without applying (dry-run)scripts/doctor.sh: System diagnostics (DNS, SSL, containers, disk, memory)scripts/apply_update.sh: Applies updates after git syncscripts/docker_cleanup.sh: Removes unused Docker resources (used bymake clean)scripts/download_top_workflows.sh: Downloads community n8n workflowsscripts/import_workflows.sh: Imports workflows fromn8n/backup/workflows/into n8n (used bymake import)scripts/restart.sh: Restarts services with proper compose file handling (used bymake restart)scripts/setup_custom_tls.sh: Configures custom TLS certificates (used bymake setup-tls); supports--removeto revert to Let's Encryptstart_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. Usespython-dotenv(dotenv_values).
Project Name: All docker-compose commands use -p localai (defined in Makefile as PROJECT_NAME := localai).
Version: Stored in VERSION file at repository root.
Installation Flow
scripts/install.sh orchestrates the installation by running numbered scripts in sequence:
01_system_preparation.sh- System updates, firewall, security hardening02_install_docker.sh- Docker and Docker Compose installation03_generate_secrets.sh- Generate passwords, API keys, bcrypt hashes04_wizard.sh- Interactive service selection (whiptail UI)05_configure_services.sh- Service-specific configuration06_run_services.sh- Start Docker Compose stack07_final_report.sh- Display credentials and URLs08_fix_permissions.sh- Fix file ownership for non-root access
The update flow (scripts/update.sh) similarly orchestrates: git fetch + reset → service selection → apply_update.sh → restart. During updates, 03_generate_secrets.sh --update adds new variables from .env.example without regenerating existing ones (preserves user-set values).
Git update modes: Default is reset (hard reset to origin). Set GIT_MODE=merge in .env for fork workflows (merges from upstream instead of hard reset). The make git-pull command uses merge mode. Git branch support is explicit: GIT_SUPPORTED_BRANCHES=("main" "develop") in git.sh; unknown branches warn and fall back to main.
Common Development Commands
Makefile Commands
make install # Full installation (runs scripts/install.sh)
make update # Update system and services (resets to origin)
make update-preview # Preview available updates (dry-run)
make git-pull # Update for forks (merges from upstream/main)
make clean # Remove unused Docker resources (preserves data)
make clean-all # Remove ALL Docker resources including data (DANGEROUS)
make logs # View logs (all services)
make logs s=<service> # View logs for specific service
make status # Show container status
make monitor # Live CPU/memory monitoring (docker stats)
make restart # Restart all services
make stop # Stop all services
make start # Start all services
make show-restarts # Show restart count per container
make doctor # Run system diagnostics (DNS, SSL, containers, disk, memory)
make import # Import n8n workflows from backup
make import n=10 # Import first N workflows only
make setup-tls # Configure custom TLS certificates
make switch-beta # Switch to develop branch and update
make switch-stable # Switch to main branch and update
make help # Show all available commands
Adding a New Service
Follow this workflow when adding a new optional service (refer to .claude/commands/add-new-service.md for complete details):
- docker-compose.yml: Add service with
profiles: ["myservice"],restart: unless-stopped. Do NOT expose ports. - Caddyfile: Add reverse proxy block using
{$MYSERVICE_HOSTNAME}. Consider if basic auth is needed. - .env.example: Add
MYSERVICE_HOSTNAME=myservice.yourdomain.comand credentials if using basic auth. - scripts/03_generate_secrets.sh: Generate passwords and bcrypt hashes. Add to
VARS_TO_GENERATEmap. - scripts/04_wizard.sh: Add service to
base_services_dataarray for wizard selection. - scripts/databases.sh: If service uses PostgreSQL, add database name to
INIT_DB_DATABASESarray. Database creation is idempotent (checks existence before creating). Note: Postiz also requirestemporalandtemporal_visibilitydatabases. - scripts/generate_welcome_page.sh: Add service to
SERVICES_ARRAYfor welcome dashboard. - welcome/app.js: Add
SERVICE_METADATAentry with name, description, icon, color, category. - scripts/07_final_report.sh: Add service URL and credentials output using
is_profile_active "myservice". - README.md: Add one-line description under "What's Included".
- CHANGELOG.md: Add entry under
## [Unreleased]→### Added(new service = minor version bump).
Always ask users if the new service requires Caddy basic auth protection.
Versioning (CHANGELOG.md)
This project uses Semantic Versioning. When updating CHANGELOG.md:
Version Format: MAJOR.MINOR.PATCH
| Type | When to bump | Examples |
|---|---|---|
| MAJOR (X.0.0) | Breaking changes that require user action | n8n 2.0 migration, config format changes, removed features |
| MINOR (0.X.0) | New services or features (backward compatible) | Adding NocoDB, new wizard options, new Makefile commands |
| PATCH (0.0.X) | Bug fixes (backward compatible) | Healthcheck fixes, proxy bypass fixes, typo corrections |
Changelog Entry Format
## [Unreleased]
## [2.6.0] - 2026-01-15
### Added
- **NewService** - Brief description of what it provides
### Changed
- Description of modified behavior
### Fixed
- Description of bug fix
After Release
- Move items from
[Unreleased]to new version section - Add comparison link at bottom of file:
[2.6.0]: https://github.com/kossakovsky/n8n-install/compare/v2.5.3...v2.6.0 - Update
[Unreleased]link to compare from new version
Important Service Details
n8n Configuration (v2.0+)
- n8n runs in
EXECUTIONS_MODE=queuewith Redis as the queue backend - OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=true: All executions (including manual tests) run on workers
- Worker-Runner Sidecar Pattern: Each worker has its own dedicated task runner
- Workers and runners are generated dynamically via
scripts/generate_n8n_workers.sh - Configuration stored in
docker-compose.n8n-workers.yml(auto-generated, gitignored) - Runner connects to its worker via
network_mode: "service:n8n-worker-N"(localhost:5679) - Runner image
n8nio/runnersmust match n8n version
- Workers and runners are generated dynamically via
- Template profile pattern:
docker-compose.ymldefinesn8n-worker-templateandn8n-runner-templatewithprofiles: ["n8n-template"](never activated directly).generate_n8n_workers.shuses these as templates to generatedocker-compose.n8n-workers.ymlwith the actual worker/runner services. - Scaling: Change
N8N_WORKER_COUNTin.envand runbash scripts/generate_n8n_workers.sh - Code node libraries: Configured via
n8n/n8n-task-runners.jsonandn8n/Dockerfile.runner:- JavaScript runner: packages installed via
pnpm addin Dockerfile.runner; allowlist inn8n-task-runners.json(NODE_FUNCTION_ALLOW_EXTERNAL,NODE_FUNCTION_ALLOW_BUILTIN); default packages:cheerio,axios,moment,lodash - Python runner: also configured in
n8n-task-runners.json; uses/opt/runners/task-runner-python/.venv/bin/pythonwithN8N_RUNNERS_STDLIB_ALLOW: "*"andN8N_RUNNERS_EXTERNAL_ALLOW: "*"
- JavaScript runner: packages installed via
- Workflows can access the host filesystem via
/data/shared(mapped to./shared) N8N_BLOCK_ENV_ACCESS_IN_NODE=falseallows Code nodes to access environment variables
Caddy Reverse Proxy
- Automatically obtains Let's Encrypt certificates when
LETSENCRYPT_EMAILis set - Hostnames are passed via environment variables (e.g.,
N8N_HOSTNAME,FLOWISE_HOSTNAME) - Basic auth uses bcrypt hashes generated by
scripts/03_generate_secrets.shvia Caddy's hash command - Never add
ports:to services in docker-compose.yml; let Caddy handle all external access - Caddy Addons (
caddy-addon/): Extend Caddy config without modifying the main Caddyfile. Files matchingsite-*.confare auto-imported (gitignored, user-created). TLS is controlled viatls-snippet.conf(all service blocks useimport service_tls). Seecaddy-addon/README.mdfor details. - Custom TLS certificates go in
certs/directory (gitignored), referenced as/etc/caddy/certs/inside the container
External Compose Files (Supabase/Dify)
Complex services like Supabase and Dify maintain their own upstream docker-compose files:
start_services.pyhandles cloning repos, preparing.envfiles, and starting services- Each external service needs:
is_*_enabled(),clone_*_repo(),prepare_*_env(),start_*()functions instart_services.py scripts/utils.shprovidesget_*_compose()getter functions andbuild_compose_files_array()includes themstop_all_services()instart_services.pychecks compose file existence (not profile) to ensure cleanup when a profile is removed- All external compose files use the same project name (
-p localai) so containers appear together docker-compose.override.yml: User customizations file (gitignored). Bothstart_services.pyandbuild_compose_files_array()inutils.shauto-detect and include it last (highest precedence). Users can override any service property without modifying tracked files.
Secret Generation
The scripts/03_generate_secrets.sh script:
- Generates random passwords, JWT secrets, API keys, and encryption keys
- Creates bcrypt password hashes using Caddy's
hash-passwordcommand - Preserves existing user-provided values in
.env - Supports different secret types via
VARS_TO_GENERATEmap:password:32,jwt,api_key,base64:64,hex:32 - When called with
--updateflag (during updates), only adds new variables without regenerating existing ones
Utility Functions (scripts/utils.sh)
Source with: source "$(dirname "$0")/utils.sh" && init_paths
Key functions:
is_profile_active "myservice"- Check if profile is enabledread_env_var "VAR_NAME"/write_env_var "VAR_NAME" "value"- .env manipulationload_env- Source .env file to make variables availableupdate_compose_profiles "profile1,profile2"- Update COMPOSE_PROFILES in .envgen_password 32/gen_hex 64/gen_base64 64- Secret generationgenerate_bcrypt_hash "password"- Create Caddy-compatible bcrypt hash (uses Caddy binary)json_escape "string"- Escape string for JSON outputwt_input,wt_password,wt_yesno,wt_msg- Whiptail dialog wrapperswt_checklist,wt_radiolist,wt_menu- Whiptail selection dialogswt_parse_choices "$result" array_name- Parse quoted checklist output safelylog_info,log_success,log_warning,log_error- Logging functionslog_header,log_subheader,log_divider,log_box- Formatted outputprint_ok,print_error,print_warning,print_info- Doctor output helpersget_real_user/get_real_user_home- Get actual user even under sudobackup_preserved_dirs/restore_preserved_dirs- Directory preservation for git updatescleanup_legacy_n8n_workers- Remove old n8n worker containers from previous naming conventionget_n8n_workers_compose/get_supabase_compose/get_dify_compose- Get compose file path if profile active AND file existsbuild_compose_files_array- Build globalCOMPOSE_FILESarray with all active compose files (main + external)
Service Profiles
Common profiles:
n8n: n8n workflow automation (includes main app, worker, runner, and import services)flowise: Flowise AI agent buildermonitoring: Prometheus, Grafana, cAdvisor, node-exporterlangfuse: Langfuse observability (includes ClickHouse, MinIO, worker, web)cpu,gpu-nvidia,gpu-amd: Ollama hardware profiles (mutually exclusive)cloudflare-tunnel: Cloudflare Tunnel for zero-trust access (seecloudflare-instructions.md)supabase: Supabase BaaS (external compose, cloned at runtime; mutually exclusive withdify)dify: Dify AI platform (external compose, cloned at runtime; mutually exclusive withsupabase)gost: HTTP/HTTPS proxy for routing AI service outbound trafficpython-runner: Internal Python execution environment (no external access)searxng,letta,lightrag,libretranslate,crawl4ai,docling,waha,comfyui,paddleocr,ragapp,gotenberg,postiz: Additional optional services
Architecture Patterns
Docker Compose YAML Anchors
docker-compose.yml defines reusable anchors at the top:
x-logging: &default-logging-json-filewithmax-size: 1m,max-file: 1x-proxy-env: &proxy-env- HTTP/HTTPS proxy vars fromGOST_PROXY_URL/GOST_NO_PROXYx-n8n: &service-n8n- Full n8n service definition (reused by workers viaextends)x-ollama: &service-ollama- Ollama service definition (reused by CPU/GPU variants)x-init-ollama: &init-ollama- Ollama model pre-puller (auto-pullsqwen2.5:7b-instruct-q4_K_Mandnomic-embed-text)x-n8n-worker-runner: &service-n8n-worker-runner- Runner template for worker generation
Healthchecks
Services should define healthchecks for proper dependency management:
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/health || exit 1"]
interval: 30s
timeout: 10s
retries: 5
Service Dependencies
Use depends_on with conditions:
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
Environment Variable Patterns
- All secrets/passwords end with
_PASSWORDor_KEY - All hostnames end with
_HOSTNAME - Password hashes end with
_PASSWORD_HASH - Use
${VAR:-default}for optional vars with defaults
Profile Activation Logic
In bash scripts, check if a profile is active:
if is_profile_active "myservice"; then
# Service-specific logic
fi
Proxy Configuration (for AI services)
Services making outbound HTTP requests to AI APIs (OpenAI, Anthropic, etc.) should use the shared proxy anchor:
x-proxy-env: &proxy-env
HTTP_PROXY: ${GOST_PROXY_URL:-}
HTTPS_PROXY: ${GOST_PROXY_URL:-}
NO_PROXY: ${GOST_NO_PROXY:-}
services:
myservice:
environment:
<<: *proxy-env # Inherit proxy settings
Important: Healthchecks must bypass proxy:
healthcheck:
test: ["CMD-SHELL", "http_proxy= https_proxy= HTTP_PROXY= HTTPS_PROXY= wget -qO- http://localhost:8080/health || exit 1"]
GOST_NO_PROXY: ALL service container names must be listed in GOST_NO_PROXY in .env.example. This prevents internal Docker network traffic from routing through the proxy. This applies to every service, not just those using <<: *proxy-env.
Welcome Page Dashboard
The welcome page (welcome/) provides a post-install dashboard showing all active services:
scripts/generate_welcome_page.sh: Generateswelcome/services.jsonwith service URLs, credentials, and metadatawelcome/app.js: ContainsSERVICE_METADATAobject defining display properties (name, description, icon, color, category)- Categories:
ai,database,monitoring,tools,infra,automation - Always use
json_escape "$VAR"when building JSON to handle special characters
Preserved Directories
Directories in PRESERVE_DIRS (defined in scripts/utils.sh) survive git updates:
python-runner/- User's custom Python code
These are backed up before git reset --hard and restored after.
Restart Behavior
scripts/restart.sh stops all services first, then starts external stacks (Supabase/Dify) separately before the main stack (10s delay between). This is required because external compose files use relative volume paths that resolve from their own directory.
Common Issues and Solutions
Service won't start after adding
- Ensure profile is added to
COMPOSE_PROFILESin.env - Check logs:
docker compose -p localai logs <service> - Verify no port conflicts (no services should publish ports)
- Ensure healthcheck is properly defined if service has dependencies
Caddy certificate issues
- DNS must be configured before installation (wildcard A record:
*.yourdomain.com) - Check Caddy logs for certificate acquisition errors
- Verify
LETSENCRYPT_EMAILis set in.env
Password hash generation fails
- Ensure Caddy container is running:
docker compose -p localai up -d caddy - Script uses:
docker exec caddy caddy hash-password --plaintext "$password"
File Locations
- Shared files accessible by n8n:
./shared(mounted as/data/sharedin n8n) - n8n backup/workflows:
n8n/backup/workflows/(mounted as/backupin n8n containers) - n8n storage: Docker volume
localai_n8n_storage - Flowise storage:
~/.flowiseon host (mounted from user's home directory, not a named volume) - Custom TLS certificates:
certs/(gitignored, mounted as/etc/caddy/certs/) - Caddy addon configs:
caddy-addon/site-*.conf(gitignored, auto-imported) - Service-specific volumes: Defined in
volumes:section at top ofdocker-compose.yml - Installation logs: stdout during script execution
- Service logs:
docker compose -p localai logs <service>
Testing Changes
Syntax Validation
# Docker Compose syntax
docker compose -p localai config --quiet
# Bash script syntax (validate all key scripts)
bash -n scripts/utils.sh
bash -n scripts/git.sh
bash -n scripts/databases.sh
bash -n scripts/telemetry.sh
bash -n scripts/03_generate_secrets.sh
bash -n scripts/04_wizard.sh
bash -n scripts/05_configure_services.sh
bash -n scripts/07_final_report.sh
bash -n scripts/generate_welcome_page.sh
bash -n scripts/generate_n8n_workers.sh
bash -n scripts/apply_update.sh
bash -n scripts/update.sh
bash -n scripts/install.sh
bash -n scripts/restart.sh
bash -n scripts/doctor.sh
bash -n scripts/setup_custom_tls.sh
bash -n scripts/docker_cleanup.sh
Full Testing
When modifying installer scripts:
- Test on a clean Ubuntu 24.04 LTS system (minimum 4GB RAM / 2 CPU)
- Verify all profile combinations work
- Check that
.envis properly generated - Confirm final report displays correct URLs and credentials
- Test update script preserves custom configurations