17 Commits

Author SHA1 Message Date
Yury Kossakovsky
19325191c3 fix(installer): skip n8n prompts when n8n profile is not active
load COMPOSE_PROFILES early in 05_configure_services.sh so
is_profile_active guards n8n workflow import and worker config
sections, avoiding confusing prompts for users who don't use n8n
2026-02-27 18:56:39 -07:00
Yury Kossakovsky
107f18296a feat: add appsmith low-code platform for internal tools
adds appsmith as an optional service with caddy reverse proxy,
auto-generated encryption secrets, wizard selection, welcome page
integration, update preview support, and final report output.
bumps version to 1.3.0.
2026-02-27 18:39:45 -07:00
Yury Kossakovsky
059e141daa fix(ragflow): correct nginx config and backend port (#41)
mount nginx config to conf.d/default.conf instead of
sites-available/default, and set SVR_HTTP_PORT to 9380
(official default) instead of 80 which conflicts with
nginx and causes 502 on api requests
2026-02-27 18:11:12 -07:00
Yury Kossakovsky
6505c5cdf4 fix(docker): limit parallel image pulls to prevent tls handshake timeout
set COMPOSE_PARALLEL_LIMIT=3 in .env.example to avoid net/http TLS
handshake timeout errors when pulling many images simultaneously
2026-02-27 16:55:40 -07:00
Yury Kossakovsky
f8e665f85f fix(comfyui): update docker image to cuda 12.8 2026-02-10 17:09:19 -07:00
Yury Kossakovsky
f2f51c6e13 docs: add agents.md with repository guidelines 2026-02-03 10:28:14 -07:00
Yury Kossakovsky
ceaa970273 docs: add missing architecture details to claude.md
document valkey/redis naming, VERSION file, GIT_MODE, caddy addons,
external compose files pattern, GOST_NO_PROXY requirement, and
n8n-template profile pattern
2026-02-03 10:28:10 -07:00
Yury Kossakovsky
6f1aaa0555 docs(changelog): release 1.2.5 2026-02-02 21:11:19 -07:00
Yury Kossakovsky
0dec31539e fix(n8n): use static ffmpeg for alpine compatibility 2026-02-02 21:04:06 -07:00
Yury Kossakovsky
b990b09681 docs: add missing scripts to key files in claude.md 2026-02-02 14:06:27 -07:00
Yury Kossakovsky
de8df8a0b7 fix(postiz): use localhost instead of docker hostname for backend_internal_url
the internal nginx in postiz container requires localhost, not the docker
service name, as this url is used for proxying within the container itself.
2026-01-30 13:50:50 -07:00
Yury Kossakovsky
543593de36 docs(gost): clarify http proxy protocol in wizard and env example
users may mistakenly use https:// for http proxies, which causes
gost to fail connecting to upstream. the protocol refers to proxy
type, not connection security.
2026-01-30 10:55:31 -07:00
Yury Kossakovsky
50bd817b56 fix(gost): add telegram domains to proxy bypass list
allows n8n telegram triggers to work when gost proxy is enabled
2026-01-29 16:11:18 -07:00
Yury Kossakovsky
611591dc0f docs(changelog): update 1.2.2 release date 2026-01-26 17:51:36 -07:00
Yury Kossakovsky
ad9c7aa57d fix(caddy): set readable permissions on custom tls certificates
docker volume mounts preserve host permissions, and caddy container
may run as different uid than host user, causing certificate read
failures with restrictive (600) permissions.
2026-01-26 17:50:35 -07:00
Yury Kossakovsky
6e283c508c fix(caddy): resolve snippet redeclaration by using site-*.conf pattern 2026-01-24 21:11:25 -07:00
Yury Kossakovsky
adc5b94f1c fix(caddy): resolve duplicate hostname error with custom tls certificates
change architecture from generating separate site blocks to using
a shared tls snippet that all services import
2026-01-24 20:23:25 -07:00
21 changed files with 449 additions and 396 deletions

View File

@@ -99,6 +99,15 @@ NEO4J_AUTH_PASSWORD=
NOCODB_JWT_SECRET=
############
# [required]
# Appsmith encryption credentials (auto-generated)
############
APPSMITH_ENCRYPTION_PASSWORD=
APPSMITH_ENCRYPTION_SALT=
############
# [required]
# Langfuse credentials
@@ -148,6 +157,7 @@ LT_PASSWORD_HASH=
USER_DOMAIN_NAME=
LETSENCRYPT_EMAIL=
APPSMITH_HOSTNAME=appsmith.yourdomain.com
COMFYUI_HOSTNAME=comfyui.yourdomain.com
DATABASUS_HOSTNAME=databasus.yourdomain.com
DIFY_HOSTNAME=dify.yourdomain.com
@@ -430,11 +440,13 @@ GOST_PROXY_URL=
# External upstream proxy (REQUIRED - asked during wizard if gost is selected)
# Examples: socks5://user:pass@proxy.com:1080, http://user:pass@proxy.com:8080
# IMPORTANT: For HTTP proxies use http://, NOT https://
# The protocol refers to proxy type, not connection security.
GOST_UPSTREAM_PROXY=
# Internal services bypass list (prevents internal Docker traffic from going through proxy)
# Includes: Docker internal networks (172.16-31.*, 10.*), Docker DNS (127.0.0.11), and all service hostnames
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,postgres,postgres:5432,redis,redis:6379,caddy,ollama,neo4j,qdrant,weaviate,clickhouse,minio,searxng,crawl4ai,gotenberg,langfuse-web,langfuse-worker,flowise,n8n,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,waha,libretranslate,paddleocr,nocodb,db,studio,kong,auth,rest,realtime,storage,imgproxy,meta,functions,analytics,vector,supavisor,gost
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,neo4j,qdrant,weaviate,clickhouse,minio,searxng,crawl4ai,gotenberg,langfuse-web,langfuse-worker,flowise,n8n,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,waha,libretranslate,paddleocr,nocodb,db,studio,kong,auth,rest,realtime,storage,imgproxy,meta,functions,analytics,vector,supavisor,gost,api.telegram.org,telegram.org,t.me,core.telegram.org
############
# Functions - Configuration for Functions
@@ -475,6 +487,14 @@ DIFY_SECRET_KEY=
DIFY_EXPOSE_NGINX_PORT=8080
DIFY_EXPOSE_NGINX_SSL_PORT=9443
############
# Docker Compose parallel limit
# Limits the number of simultaneous Docker image pulls to prevent
# "net/http: TLS handshake timeout" errors when many services are selected.
# Increase this value if you have a fast network connection.
############
COMPOSE_PARALLEL_LIMIT=3
###########################################################################################
COMPOSE_PROFILES="n8n,portainer,monitoring,databasus"
PROMETHEUS_PASSWORD_HASH=

31
AGENTS.md Normal file
View File

@@ -0,0 +1,31 @@
# Repository Guidelines
## Project Structure & Module Organization
- Core runtime config lives at the repo root: `docker-compose.yml`, `docker-compose.n8n-workers.yml`, and `Caddyfile`.
- Installer and maintenance logic is in `scripts/` (install, update, doctor, cleanup, and helpers).
- Service-specific assets are grouped by folder (examples: `n8n/`, `grafana/`, `prometheus/`, `searxng/`, `ragflow/`, `python-runner/`, `welcome/`).
- Shared files for workflows are stored in `shared/` and mounted inside containers as `/data/shared`.
## Build, Test, and Development Commands
- `make install`: run the full installation wizard.
- `make update` or `make git-pull`: refresh images and configuration (fork-friendly via `make git-pull`).
- `make logs s=<service>`: tail a specific services logs (example: `make logs s=n8n`).
- `make doctor`: run system checks for DNS/SSL/containers.
- `make restart`, `make stop`, `make start`, `make status`: manage the compose stack.
- `make clean` or `make clean-all`: remove unused Docker resources (`clean-all` is destructive).
## Coding Style & Naming Conventions
- Bash scripts in `scripts/` use `#!/bin/bash`, 4-space indentation, and uppercase constants. Match existing formatting.
- Environment variable patterns are consistent: hostnames use `_HOSTNAME`, secrets use `_PASSWORD` or `_KEY`, and bcrypt hashes use `_PASSWORD_HASH`.
- Services should not publish ports directly; external access goes through Caddy.
## Testing Guidelines
- There is no unit-test suite. Use syntax checks instead:
- `docker compose -p localai config --quiet`
- `bash -n scripts/install.sh` (and other edited scripts)
- For installer changes, validate on a clean Ubuntu 24.04 LTS host and confirm profile selections start correctly.
## Commit & Pull Request Guidelines
- Commit messages follow Conventional Commits: `type(scope): summary` (examples in history include `fix(caddy): ...`, `docs(readme): ...`, `feat(postiz): ...`).
- PRs should include a short summary, affected services/profiles, and test commands run.
- Update `README.md` and `CHANGELOG.md` for user-facing changes or new services.

View File

@@ -2,6 +2,51 @@
## [Unreleased]
## [1.3.1] - 2026-02-27
### Fixed
- **Installer** - Skip n8n workflow import and worker configuration prompts when n8n profile is not selected
## [1.3.0] - 2026-02-27
### Added
- **Appsmith** - Low-code platform for building internal tools, dashboards, and admin panels
## [1.2.8] - 2026-02-27
### Fixed
- **Ragflow** - Fix nginx config mount path (`sites-available/default``conf.d/default.conf`) to resolve default "Welcome to nginx!" page (#41)
## [1.2.7] - 2026-02-27
### Fixed
- **Docker** - Limit parallel image pulls (`COMPOSE_PARALLEL_LIMIT=3`) to prevent `TLS handshake timeout` errors when many services are selected
## [1.2.6] - 2026-02-10
### Changed
- **ComfyUI** - Update Docker image to CUDA 12.8 (`cu128-slim`)
## [1.2.5] - 2026-02-03
### Fixed
- **n8n** - Use static ffmpeg binaries for Alpine/musl compatibility (fixes glibc errors)
## [1.2.4] - 2026-01-30
### Fixed
- **Postiz** - Fix `BACKEND_INTERNAL_URL` to use `localhost` instead of Docker hostname (internal nginx requires localhost)
## [1.2.3] - 2026-01-29
### Fixed
- **Gost proxy** - Add Telegram domains to `GOST_NO_PROXY` bypass list for n8n Telegram triggers
## [1.2.2] - 2026-01-26
### Fixed
- **Custom TLS** - Fix duplicate hostname error when using custom certificates. Changed architecture from generating separate site blocks to using a shared TLS snippet that all services import.
## [1.2.1] - 2026-01-16
### Added

View File

@@ -10,7 +10,7 @@ This is **n8n-install**, a Docker Compose-based installer that provides a compre
- **Profile-based service management**: Services are activated via Docker Compose profiles (e.g., `n8n`, `flowise`, `monitoring`). Profiles are stored in the `.env` file's `COMPOSE_PROFILES` variable.
- **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, Redis/Valkey, Caddy) are always included. Other services are optional and selected during installation.
- **Shared secrets**: Core services (Postgres, Valkey (Redis-compatible, container named `redis` for backward compatibility), Caddy) are always included. Other services are optional and selected during installation.
- **Queue-based n8n**: n8n runs in `queue` mode with Redis, Postgres, and dynamically scaled workers (`N8N_WORKER_COUNT`).
### Key Files
@@ -40,9 +40,13 @@ This is **n8n-install**, a Docker Compose-based installer that provides a compre
- `scripts/docker_cleanup.sh`: Removes unused Docker resources (used by `make clean`)
- `scripts/download_top_workflows.sh`: Downloads community n8n workflows
- `scripts/import_workflows.sh`: Imports workflows from `n8n/backup/workflows/` into n8n (used by `make import`)
- `scripts/restart.sh`: Restarts services with proper compose file handling (used by `make restart`)
- `scripts/setup_custom_tls.sh`: Configures custom TLS certificates (used by `make setup-tls`)
**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:
@@ -58,6 +62,8 @@ This is **n8n-install**, a Docker Compose-based installer that provides a compre
The update flow (`scripts/update.sh`) similarly orchestrates: git fetch + reset → service selection → `apply_update.sh` → restart.
**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.
## Common Development Commands
### Makefile Commands
@@ -156,6 +162,7 @@ This project uses [Semantic Versioning](https://semver.org/). When updating `CHA
- 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/runners` must match n8n version
- **Template profile pattern**: `docker-compose.yml` defines `n8n-worker-template` and `n8n-runner-template` with `profiles: ["n8n-template"]` (never activated directly). `generate_n8n_workers.sh` uses these as templates to generate `docker-compose.n8n-workers.yml` with the actual worker/runner services.
- **Scaling**: Change `N8N_WORKER_COUNT` in `.env` and run `bash scripts/generate_n8n_workers.sh`
- **Code node libraries**: Configured via `n8n/n8n-task-runners.json` and `n8n/Dockerfile.runner`:
- JS packages installed via `pnpm add` in Dockerfile.runner
@@ -170,6 +177,16 @@ This project uses [Semantic Versioning](https://semver.org/). When updating `CHA
- Hostnames are passed via environment variables (e.g., `N8N_HOSTNAME`, `FLOWISE_HOSTNAME`)
- Basic auth uses bcrypt hashes generated by `scripts/03_generate_secrets.sh` via 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 matching `site-*.conf` are auto-imported. TLS is controlled via `tls-snippet.conf` (all service blocks use `import service_tls`). See `caddy-addon/README.md` for details.
### External Compose Files (Supabase/Dify)
Complex services like Supabase and Dify maintain their own upstream docker-compose files:
- `start_services.py` handles cloning repos, preparing `.env` files, and starting services
- Each external service needs: `is_*_enabled()`, `clone_*_repo()`, `prepare_*_env()`, `start_*()` functions in `start_services.py`
- `scripts/utils.sh` provides `get_*_compose()` getter functions and `build_compose_files_array()` includes them
- `stop_all_services()` in `start_services.py` checks 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
### Secret Generation
@@ -276,6 +293,8 @@ 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:

View File

@@ -3,30 +3,44 @@
email {$LETSENCRYPT_EMAIL}
}
# Import TLS snippet (must be before service blocks)
# Default: Let's Encrypt automatic certificates
# Custom: Run 'make setup-tls' to use your own certificates
import /etc/caddy/addons/tls-snippet.conf
# Appsmith
{$APPSMITH_HOSTNAME} {
import service_tls
reverse_proxy appsmith:80
}
# N8N
{$N8N_HOSTNAME} {
# For domains, Caddy will automatically use Let's Encrypt
# For localhost/port addresses, HTTPS won't be enabled
import service_tls
reverse_proxy n8n:5678
}
# Open WebUI
{$WEBUI_HOSTNAME} {
import service_tls
reverse_proxy open-webui:8080
}
# Flowise
{$FLOWISE_HOSTNAME} {
import service_tls
reverse_proxy flowise:3001
}
# Dify
{$DIFY_HOSTNAME} {
import service_tls
reverse_proxy nginx:80
}
# RAGApp
{$RAGAPP_HOSTNAME} {
import service_tls
basic_auth {
{$RAGAPP_USERNAME} {$RAGAPP_PASSWORD_HASH}
}
@@ -35,37 +49,38 @@
# RAGFlow
{$RAGFLOW_HOSTNAME} {
import service_tls
reverse_proxy ragflow:80
}
# Langfuse
{$LANGFUSE_HOSTNAME} {
import service_tls
reverse_proxy langfuse-web:3000
}
# # Ollama API
# {$OLLAMA_HOSTNAME} {
# reverse_proxy ollama:11434
# }
# Supabase
{$SUPABASE_HOSTNAME} {
import service_tls
reverse_proxy kong:8000
}
# Grafana
{$GRAFANA_HOSTNAME} {
import service_tls
reverse_proxy grafana:3000
}
# WAHA (WhatsApp HTTP API)
{$WAHA_HOSTNAME} {
import service_tls
reverse_proxy waha:3000
}
# Prometheus
{$PROMETHEUS_HOSTNAME} {
basic_auth {
import service_tls
basic_auth {
{$PROMETHEUS_USERNAME} {$PROMETHEUS_PASSWORD_HASH}
}
reverse_proxy prometheus:9090
@@ -73,16 +88,19 @@
# Portainer
{$PORTAINER_HOSTNAME} {
import service_tls
reverse_proxy portainer:9000
}
# Postiz
{$POSTIZ_HOSTNAME} {
import service_tls
reverse_proxy postiz:5000
}
# Temporal UI (workflow orchestration for Postiz)
{$TEMPORAL_UI_HOSTNAME} {
import service_tls
basic_auth {
{$TEMPORAL_UI_USERNAME} {$TEMPORAL_UI_PASSWORD_HASH}
}
@@ -91,31 +109,37 @@
# Databasus
{$DATABASUS_HOSTNAME} {
import service_tls
reverse_proxy databasus:4005
}
# Letta
{$LETTA_HOSTNAME} {
import service_tls
reverse_proxy letta:8283
}
# LightRAG (Graph-based RAG with Knowledge Extraction)
{$LIGHTRAG_HOSTNAME} {
import service_tls
reverse_proxy lightrag:9621
}
# Weaviate
{$WEAVIATE_HOSTNAME} {
import service_tls
reverse_proxy weaviate:8080
}
# Qdrant
{$QDRANT_HOSTNAME} {
import service_tls
reverse_proxy qdrant:6333
}
# ComfyUI
{$COMFYUI_HOSTNAME} {
import service_tls
basic_auth {
{$COMFYUI_USERNAME} {$COMFYUI_PASSWORD_HASH}
}
@@ -124,6 +148,7 @@
# LibreTranslate (Self-hosted Translation API)
{$LT_HOSTNAME} {
import service_tls
basic_auth {
{$LT_USERNAME} {$LT_PASSWORD_HASH}
}
@@ -132,21 +157,25 @@
# Neo4j
{$NEO4J_HOSTNAME} {
import service_tls
reverse_proxy neo4j:7474
}
# Neo4j Bolt Protocol (wss)
https://{$NEO4J_HOSTNAME}:7687 {
import service_tls
reverse_proxy neo4j:7687
}
# NocoDB
{$NOCODB_HOSTNAME} {
import service_tls
reverse_proxy nocodb:8080
}
# PaddleOCR (PaddleX Basic Serving)
{$PADDLEOCR_HOSTNAME} {
import service_tls
basic_auth {
{$PADDLEOCR_USERNAME} {$PADDLEOCR_PASSWORD_HASH}
}
@@ -155,6 +184,7 @@ https://{$NEO4J_HOSTNAME}:7687 {
# Docling (Document Conversion API)
{$DOCLING_HOSTNAME} {
import service_tls
basic_auth {
{$DOCLING_USERNAME} {$DOCLING_PASSWORD_HASH}
}
@@ -174,6 +204,7 @@ http://{$WELCOME_HOSTNAME} {
# HTTPS block for direct access
{$WELCOME_HOSTNAME} {
import service_tls
basic_auth {
{$WELCOME_USERNAME} {$WELCOME_PASSWORD_HASH}
}
@@ -182,10 +213,12 @@ http://{$WELCOME_HOSTNAME} {
try_files {path} /index.html
}
import /etc/caddy/addons/*.conf
# Import custom site addons
import /etc/caddy/addons/site-*.conf
# # SearXNG
# SearXNG
{$SEARXNG_HOSTNAME} {
import service_tls
@protected not remote_ip 127.0.0.0/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 100.64.0.0/10
basic_auth @protected {

View File

@@ -56,6 +56,8 @@ This setup provides a comprehensive suite of cutting-edge services, all pre-conf
The installer also makes the following powerful open-source tools **available for you to select and deploy** via an interactive wizard during setup:
✅ [**Appsmith**](https://www.appsmith.com/) - An open-source low-code platform for building internal tools, dashboards, and admin panels with a drag-and-drop UI builder.
✅ [**n8n**](https://n8n.io/) - A low-code platform with over 400 integrations and advanced AI components to automate workflows.
✅ [**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.
@@ -179,6 +181,7 @@ After successful installation, your services are up and running! Here's how to g
The installation script provided a summary report with all access URLs and credentials. Please refer to that report. The main services will be available at the following addresses (replace `yourdomain.com` with your actual domain):
- **n8n:** `n8n.yourdomain.com` (Log in with the email address you provided during installation and the initial password from the summary report. You may be prompted to change this password on first login.)
- **Appsmith:** `appsmith.yourdomain.com` (Low-code app builder)
- **ComfyUI:** `comfyui.yourdomain.com` (Node-based Stable Diffusion UI)
- **Databasus:** `databasus.yourdomain.com`
- **Dify:** `dify.yourdomain.com` (AI application development platform with comprehensive LLMOps capabilities)

View File

@@ -1 +1 @@
1.0.0
1.3.1

View File

@@ -2,7 +2,7 @@
This directory allows you to extend or override Caddy configuration without modifying the main `Caddyfile`.
All `.conf` files in this directory are automatically imported via `import /etc/caddy/addons/*.conf` at the end of the main Caddyfile.
Files matching `site-*.conf` in this directory are automatically imported via `import /etc/caddy/addons/site-*.conf` in the main Caddyfile.
## Use Cases
@@ -15,6 +15,23 @@ All `.conf` files in this directory are automatically imported via `import /etc/
For corporate/internal deployments where Let's Encrypt is not available, you can use your own certificates.
### How It Works
The main `Caddyfile` imports a TLS snippet that all service blocks use:
```caddy
# In Caddyfile (top)
import /etc/caddy/addons/tls-snippet.conf
# In each service block
{$N8N_HOSTNAME} {
import service_tls # <-- Uses the snippet
reverse_proxy n8n:5678
}
```
By default, the snippet is empty (Let's Encrypt is used). When you run `make setup-tls`, the snippet is updated with your certificate paths.
### Quick Setup
1. Place your certificates in the `certs/` directory:
@@ -28,42 +45,22 @@ For corporate/internal deployments where Let's Encrypt is not available, you can
make setup-tls
```
3. Restart Caddy:
```bash
docker compose -p localai restart caddy
```
3. The script will:
- Update `caddy-addon/tls-snippet.conf` with your certificate paths
- Optionally restart Caddy to apply changes
### Manual Setup
### Reset to Let's Encrypt
1. Copy the example file:
```bash
cp caddy-addon/custom-tls.conf.example caddy-addon/custom-tls.conf
```
To switch back to automatic Let's Encrypt certificates:
2. Edit `custom-tls.conf` with your hostnames and certificate paths
3. Place certificates in `certs/` directory
4. Restart Caddy:
```bash
docker compose -p localai restart caddy
```
## How Site Override Works
When you define a site block in an addon file with the same hostname as the main Caddyfile, Caddy will use **both** configurations. To completely override a site, use the exact same hostname.
Example: To override `n8n.yourdomain.com` with a custom certificate:
```
# caddy-addon/custom-tls.conf
n8n.internal.company.com {
tls /etc/caddy/certs/wildcard.crt /etc/caddy/certs/wildcard.key
reverse_proxy n8n:5678
}
```bash
make setup-tls --remove
```
Make sure your `.env` file has `N8N_HOSTNAME=n8n.internal.company.com`.
Or run directly:
```bash
bash scripts/setup_custom_tls.sh --remove
```
## File Structure
@@ -71,8 +68,9 @@ Make sure your `.env` file has `N8N_HOSTNAME=n8n.internal.company.com`.
caddy-addon/
├── .gitkeep # Keeps directory in git
├── README.md # This file
├── custom-tls.conf.example # Example for custom certificates
── custom-tls.conf # Your custom config (gitignored)
├── tls-snippet.conf.example # Template for TLS snippet (tracked in git)
── tls-snippet.conf # Your TLS config (gitignored, auto-created)
└── site-*.conf # Your custom addons (gitignored, must start with "site-")
certs/
├── .gitkeep # Keeps directory in git
@@ -80,11 +78,26 @@ certs/
└── wildcard.key # Your private key (gitignored)
```
## Adding Custom Addons
You can create `site-*.conf` files for custom Caddy configurations. They will be automatically loaded by the main Caddyfile.
**Important:** Custom addon files MUST start with `site-` prefix to be loaded (e.g., `site-custom.conf`, `site-myapp.conf`).
Example: `caddy-addon/site-custom-headers.conf`
```caddy
# Add custom headers to all responses
(custom_headers) {
header X-Custom-Header "My Value"
}
```
## Important Notes
- Files in `caddy-addon/*.conf` are gitignored (preserved during updates)
- `tls-snippet.conf.example` is tracked in git (template with default Let's Encrypt behavior)
- `tls-snippet.conf` is gitignored and auto-created from template (preserved during updates)
- `site-*.conf` files are gitignored (preserved during updates)
- Files in `certs/` are gitignored (certificates are not committed)
- Example files (`*.example`) are tracked in git
- Caddy validates configuration on startup - check logs if it fails:
```bash
docker compose -p localai logs caddy

View File

@@ -1,114 +0,0 @@
# Custom TLS Configuration for Corporate/Internal Certificates
#
# This file provides examples for using your own TLS certificates instead of Let's Encrypt.
# Copy this file to custom-tls.conf and modify as needed.
#
# Prerequisites:
# 1. Place your certificate files in the ./certs/ directory
# 2. Update .env hostnames to match your internal domain
# 3. Restart Caddy: docker compose -p localai restart caddy
# =============================================================================
# Option 1: Reusable TLS snippet (recommended for wildcard certificates)
# =============================================================================
# Define once, import in each service block
(custom_tls) {
tls /etc/caddy/certs/wildcard.crt /etc/caddy/certs/wildcard.key
}
# Then for each service you want to override:
#
# n8n.internal.company.com {
# import custom_tls
# reverse_proxy n8n:5678
# }
#
# flowise.internal.company.com {
# import custom_tls
# reverse_proxy flowise:3001
# }
# =============================================================================
# Option 2: Individual service configuration
# =============================================================================
# Use when you have different certificates for different services
# n8n.internal.company.com {
# tls /etc/caddy/certs/n8n.crt /etc/caddy/certs/n8n.key
# reverse_proxy n8n:5678
# }
# =============================================================================
# Option 3: Internal CA with auto-reload
# =============================================================================
# Caddy can auto-reload certificates when they change
# n8n.internal.company.com {
# tls /etc/caddy/certs/cert.pem /etc/caddy/certs/key.pem {
# # Optional: specify CA certificate for client verification
# # client_auth {
# # mode require_and_verify
# # trusted_ca_cert_file /etc/caddy/certs/ca.pem
# # }
# }
# reverse_proxy n8n:5678
# }
# =============================================================================
# Full Example: All common services with wildcard certificate
# =============================================================================
# Uncomment and modify the hostnames to match your .env configuration
# # N8N
# n8n.internal.company.com {
# import custom_tls
# reverse_proxy n8n:5678
# }
# # Flowise
# flowise.internal.company.com {
# import custom_tls
# reverse_proxy flowise:3001
# }
# # Open WebUI
# webui.internal.company.com {
# import custom_tls
# reverse_proxy open-webui:8080
# }
# # Grafana
# grafana.internal.company.com {
# import custom_tls
# reverse_proxy grafana:3000
# }
# # Portainer
# portainer.internal.company.com {
# import custom_tls
# reverse_proxy portainer:9000
# }
# # Langfuse
# langfuse.internal.company.com {
# import custom_tls
# reverse_proxy langfuse-web:3000
# }
# # Supabase
# supabase.internal.company.com {
# import custom_tls
# reverse_proxy kong:8000
# }
# # Welcome Page (with basic auth preserved)
# welcome.internal.company.com {
# import custom_tls
# basic_auth {
# {$WELCOME_USERNAME} {$WELCOME_PASSWORD_HASH}
# }
# root * /srv/welcome
# file_server
# try_files {path} /index.html
# }

View File

@@ -0,0 +1,10 @@
# TLS Configuration Snippet
# Imported by all service blocks in the main Caddyfile.
#
# Default: Empty (uses Let's Encrypt automatic certificates)
# Custom: Overwritten by 'make setup-tls' with your certificate paths
# Reset: Run 'make setup-tls --remove' to restore Let's Encrypt
(service_tls) {
# Default: Let's Encrypt automatic certificates (empty = no override)
}

View File

@@ -1,4 +1,5 @@
volumes:
appsmith_data:
caddy-config:
caddy-data:
comfyui_data:
@@ -144,6 +145,26 @@ x-n8n-worker-runner: &service-n8n-worker-runner
N8N_RUNNERS_TASK_BROKER_URI: http://127.0.0.1:5679
services:
appsmith:
image: appsmith/appsmith-ce:release
container_name: appsmith
profiles: ["appsmith"]
restart: unless-stopped
logging: *default-logging
environment:
<<: *proxy-env
APPSMITH_ENCRYPTION_PASSWORD: ${APPSMITH_ENCRYPTION_PASSWORD}
APPSMITH_ENCRYPTION_SALT: ${APPSMITH_ENCRYPTION_SALT}
APPSMITH_DISABLE_TELEMETRY: "true"
volumes:
- appsmith_data:/appsmith-stacks
healthcheck:
test: ["CMD-SHELL", "http_proxy= https_proxy= HTTP_PROXY= HTTPS_PROXY= wget -qO- http://localhost/api/v1/health || exit 1"]
interval: 30s
timeout: 10s
retries: 5
start_period: 120s
flowise:
image: flowiseai/flowise
restart: unless-stopped
@@ -318,6 +339,7 @@ services:
- caddy-data:/data:rw
- caddy-config:/config:rw
environment:
APPSMITH_HOSTNAME: ${APPSMITH_HOSTNAME}
COMFYUI_HOSTNAME: ${COMFYUI_HOSTNAME}
COMFYUI_PASSWORD_HASH: ${COMFYUI_PASSWORD_HASH}
COMFYUI_USERNAME: ${COMFYUI_USERNAME}
@@ -836,7 +858,7 @@ services:
restart: always
environment:
<<: *proxy-env
BACKEND_INTERNAL_URL: http://postiz:3000
BACKEND_INTERNAL_URL: http://localhost:3000
DATABASE_URL: "postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/${POSTIZ_DB_NAME:-postiz}?schema=postiz"
DISABLE_REGISTRATION: ${POSTIZ_DISABLE_REGISTRATION}
FRONTEND_URL: ${POSTIZ_HOSTNAME:+https://}${POSTIZ_HOSTNAME}
@@ -908,7 +930,7 @@ services:
start_period: 60s
comfyui:
image: yanwk/comfyui-boot:cu124-slim
image: yanwk/comfyui-boot:cu128-slim
container_name: comfyui
profiles: ["comfyui"]
restart: unless-stopped
@@ -1030,10 +1052,10 @@ services:
REDIS_HOST: ragflow-redis
REDIS_PASSWORD: ${RAGFLOW_REDIS_PASSWORD}
REDIS_PORT: 6379
SVR_HTTP_PORT: 80
SVR_HTTP_PORT: 9380
volumes:
- ragflow_data:/ragflow
- ./ragflow/nginx.conf:/etc/nginx/sites-available/default:ro
- ./ragflow/nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
ragflow-elasticsearch:
condition: service_healthy

View File

@@ -1,9 +1,11 @@
# Stage 1: Get static ffmpeg binaries (statically linked, works on Alpine/musl)
FROM mwader/static-ffmpeg:latest AS ffmpeg
# Stage 2: Build final n8n image with ffmpeg
FROM n8nio/n8n:stable
USER root
# Install static ffmpeg binary from BtbN GitHub releases
RUN wget -qO- --tries=3 --timeout=60 https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-linux64-gpl.tar.xz | \
tar -xJC /tmp && \
mv /tmp/ffmpeg-master-latest-linux64-gpl/bin/ffmpeg /tmp/ffmpeg-master-latest-linux64-gpl/bin/ffprobe /usr/local/bin/ && \
rm -rf /tmp/ffmpeg-*
# Copy static ffmpeg binaries from the ffmpeg stage
COPY --from=ffmpeg /ffmpeg /usr/local/bin/ffmpeg
COPY --from=ffmpeg /ffprobe /usr/local/bin/ffprobe
USER node

View File

@@ -74,6 +74,8 @@ USER_INPUT_VARS=(
# Variables to generate: varName="type:length"
# Types: password (alphanum), secret (base64), hex, base64, alphanum
declare -A VARS_TO_GENERATE=(
["APPSMITH_ENCRYPTION_PASSWORD"]="password:32"
["APPSMITH_ENCRYPTION_SALT"]="password:32"
["CLICKHOUSE_PASSWORD"]="password:32"
["COMFYUI_PASSWORD"]="password:32" # Added ComfyUI basic auth password
["DASHBOARD_PASSWORD"]="password:32" # Supabase Dashboard

View File

@@ -38,6 +38,7 @@ current_profiles_for_matching=",$CURRENT_PROFILES_VALUE,"
# --- Define available services and their descriptions ---
# Base service definitions (tag, description)
base_services_data=(
"appsmith" "Appsmith (Low-code Platform for Internal Tools & Dashboards)"
"cloudflare-tunnel" "Cloudflare Tunnel (Zero-Trust Secure Access)"
"comfyui" "ComfyUI (Node-based Stable Diffusion UI)"
"crawl4ai" "Crawl4ai (Web Crawler for AI)"
@@ -215,7 +216,7 @@ if [ $gost_selected -eq 1 ]; then
EXISTING_UPSTREAM=$(read_env_var "GOST_UPSTREAM_PROXY")
GOST_UPSTREAM_INPUT=$(wt_input "Gost Upstream Proxy" \
"Enter your external proxy URL for geo-bypass.\n\nExamples:\n socks5://user:pass@proxy.com:1080\n http://user:pass@proxy.com:8080\n\nThis proxy should be located outside restricted regions." \
"Enter your external proxy URL for geo-bypass.\n\nExamples:\n socks5://user:pass@proxy.com:1080\n http://user:pass@proxy.com:8080\n\nIMPORTANT: For HTTP proxies use http://, NOT https://.\nThe protocol refers to proxy type, not connection security.\n\nThis proxy should be located outside restricted regions." \
"$EXISTING_UPSTREAM") || true
if [ -n "$GOST_UPSTREAM_INPUT" ]; then

View File

@@ -27,6 +27,10 @@ init_paths
# Ensure .env exists
ensure_file_exists "$ENV_FILE"
# Load COMPOSE_PROFILES early so is_profile_active works for all sections
COMPOSE_PROFILES_VALUE="$(read_env_var COMPOSE_PROFILES)"
COMPOSE_PROFILES="$COMPOSE_PROFILES_VALUE"
# ----------------------------------------------------------------
# Prompt for OpenAI API key (optional) using .env value as source of truth
# ----------------------------------------------------------------
@@ -48,87 +52,89 @@ fi
# ----------------------------------------------------------------
# Logic for n8n workflow import (RUN_N8N_IMPORT)
# ----------------------------------------------------------------
log_subheader "n8n Workflow Import"
final_run_n8n_import_decision="false"
require_whiptail
if wt_yesno "Import n8n Workflows" "Import ~300 ready-made n8n workflows now? This can take ~30 minutes." "no"; then
final_run_n8n_import_decision="true"
else
if is_profile_active "n8n"; then
log_subheader "n8n Workflow Import"
final_run_n8n_import_decision="false"
fi
require_whiptail
if wt_yesno "Import n8n Workflows" "Import ~300 ready-made n8n workflows now? This can take ~30 minutes." "no"; then
final_run_n8n_import_decision="true"
else
final_run_n8n_import_decision="false"
fi
# Persist RUN_N8N_IMPORT to .env
write_env_var "RUN_N8N_IMPORT" "$final_run_n8n_import_decision"
# Persist RUN_N8N_IMPORT to .env
write_env_var "RUN_N8N_IMPORT" "$final_run_n8n_import_decision"
else
write_env_var "RUN_N8N_IMPORT" "false"
fi
# ----------------------------------------------------------------
# Prompt for number of n8n workers
# ----------------------------------------------------------------
log_subheader "n8n Worker Configuration"
EXISTING_N8N_WORKER_COUNT="$(read_env_var N8N_WORKER_COUNT)"
require_whiptail
if [[ -n "$EXISTING_N8N_WORKER_COUNT" ]]; then
N8N_WORKER_COUNT_CURRENT="$EXISTING_N8N_WORKER_COUNT"
N8N_WORKER_COUNT_INPUT_RAW=$(wt_input "n8n Workers (instances)" "Enter new number of n8n workers, or leave as current ($N8N_WORKER_COUNT_CURRENT)." "") || true
if [[ -z "$N8N_WORKER_COUNT_INPUT_RAW" ]]; then
N8N_WORKER_COUNT="$N8N_WORKER_COUNT_CURRENT"
else
if [[ "$N8N_WORKER_COUNT_INPUT_RAW" =~ ^0*[1-9][0-9]*$ ]]; then
N8N_WORKER_COUNT_TEMP="$((10#$N8N_WORKER_COUNT_INPUT_RAW))"
if [[ "$N8N_WORKER_COUNT_TEMP" -ge 1 ]]; then
if wt_yesno "Confirm Workers" "Update n8n workers to $N8N_WORKER_COUNT_TEMP?" "yes"; then
N8N_WORKER_COUNT="$N8N_WORKER_COUNT_TEMP"
if is_profile_active "n8n"; then
log_subheader "n8n Worker Configuration"
EXISTING_N8N_WORKER_COUNT="$(read_env_var N8N_WORKER_COUNT)"
require_whiptail
if [[ -n "$EXISTING_N8N_WORKER_COUNT" ]]; then
N8N_WORKER_COUNT_CURRENT="$EXISTING_N8N_WORKER_COUNT"
N8N_WORKER_COUNT_INPUT_RAW=$(wt_input "n8n Workers (instances)" "Enter new number of n8n workers, or leave as current ($N8N_WORKER_COUNT_CURRENT)." "") || true
if [[ -z "$N8N_WORKER_COUNT_INPUT_RAW" ]]; then
N8N_WORKER_COUNT="$N8N_WORKER_COUNT_CURRENT"
else
if [[ "$N8N_WORKER_COUNT_INPUT_RAW" =~ ^0*[1-9][0-9]*$ ]]; then
N8N_WORKER_COUNT_TEMP="$((10#$N8N_WORKER_COUNT_INPUT_RAW))"
if [[ "$N8N_WORKER_COUNT_TEMP" -ge 1 ]]; then
if wt_yesno "Confirm Workers" "Update n8n workers to $N8N_WORKER_COUNT_TEMP?" "yes"; then
N8N_WORKER_COUNT="$N8N_WORKER_COUNT_TEMP"
else
N8N_WORKER_COUNT="$N8N_WORKER_COUNT_CURRENT"
log_info "Change declined. Keeping N8N_WORKER_COUNT at $N8N_WORKER_COUNT."
fi
else
log_warning "Invalid input '$N8N_WORKER_COUNT_INPUT_RAW'. Number must be positive. Keeping $N8N_WORKER_COUNT_CURRENT."
N8N_WORKER_COUNT="$N8N_WORKER_COUNT_CURRENT"
log_info "Change declined. Keeping N8N_WORKER_COUNT at $N8N_WORKER_COUNT."
fi
else
log_warning "Invalid input '$N8N_WORKER_COUNT_INPUT_RAW'. Number must be positive. Keeping $N8N_WORKER_COUNT_CURRENT."
log_warning "Invalid input '$N8N_WORKER_COUNT_INPUT_RAW'. Please enter a positive integer. Keeping $N8N_WORKER_COUNT_CURRENT."
N8N_WORKER_COUNT="$N8N_WORKER_COUNT_CURRENT"
fi
else
log_warning "Invalid input '$N8N_WORKER_COUNT_INPUT_RAW'. Please enter a positive integer. Keeping $N8N_WORKER_COUNT_CURRENT."
N8N_WORKER_COUNT="$N8N_WORKER_COUNT_CURRENT"
fi
fi
else
while true; do
N8N_WORKER_COUNT_INPUT_RAW=$(wt_input "n8n Workers" "Enter number of n8n workers to run (default 1)." "1") || true
N8N_WORKER_COUNT_CANDIDATE="${N8N_WORKER_COUNT_INPUT_RAW:-1}"
if [[ "$N8N_WORKER_COUNT_CANDIDATE" =~ ^0*[1-9][0-9]*$ ]]; then
N8N_WORKER_COUNT_VALIDATED="$((10#$N8N_WORKER_COUNT_CANDIDATE))"
if [[ "$N8N_WORKER_COUNT_VALIDATED" -ge 1 ]]; then
if wt_yesno "Confirm Workers" "Run $N8N_WORKER_COUNT_VALIDATED n8n worker(s)?" "yes"; then
N8N_WORKER_COUNT="$N8N_WORKER_COUNT_VALIDATED"
break
else
while true; do
N8N_WORKER_COUNT_INPUT_RAW=$(wt_input "n8n Workers" "Enter number of n8n workers to run (default 1)." "1") || true
N8N_WORKER_COUNT_CANDIDATE="${N8N_WORKER_COUNT_INPUT_RAW:-1}"
if [[ "$N8N_WORKER_COUNT_CANDIDATE" =~ ^0*[1-9][0-9]*$ ]]; then
N8N_WORKER_COUNT_VALIDATED="$((10#$N8N_WORKER_COUNT_CANDIDATE))"
if [[ "$N8N_WORKER_COUNT_VALIDATED" -ge 1 ]]; then
if wt_yesno "Confirm Workers" "Run $N8N_WORKER_COUNT_VALIDATED n8n worker(s)?" "yes"; then
N8N_WORKER_COUNT="$N8N_WORKER_COUNT_VALIDATED"
break
fi
else
log_error "Number of workers must be a positive integer."
fi
else
log_error "Number of workers must be a positive integer."
log_error "Invalid input '$N8N_WORKER_COUNT_CANDIDATE'. Please enter a positive integer (e.g., 1, 2)."
fi
else
log_error "Invalid input '$N8N_WORKER_COUNT_CANDIDATE'. Please enter a positive integer (e.g., 1, 2)."
fi
done
done
fi
# Ensure N8N_WORKER_COUNT is definitely set (should be by logic above)
N8N_WORKER_COUNT="${N8N_WORKER_COUNT:-1}"
# Persist N8N_WORKER_COUNT to .env
write_env_var "N8N_WORKER_COUNT" "$N8N_WORKER_COUNT"
# Generate worker-runner pairs configuration
# Each worker gets its own dedicated task runner sidecar
log_info "Generating n8n worker-runner pairs configuration..."
bash "$SCRIPT_DIR/generate_n8n_workers.sh"
fi
# Ensure N8N_WORKER_COUNT is definitely set (should be by logic above)
N8N_WORKER_COUNT="${N8N_WORKER_COUNT:-1}"
# Persist N8N_WORKER_COUNT to .env
write_env_var "N8N_WORKER_COUNT" "$N8N_WORKER_COUNT"
# Generate worker-runner pairs configuration
# Each worker gets its own dedicated task runner sidecar
log_info "Generating n8n worker-runner pairs configuration..."
bash "$SCRIPT_DIR/generate_n8n_workers.sh"
# ----------------------------------------------------------------
# Cloudflare Tunnel Token (if cloudflare-tunnel profile is active)
# ----------------------------------------------------------------
COMPOSE_PROFILES_VALUE="$(read_env_var COMPOSE_PROFILES)"
# Set COMPOSE_PROFILES for is_profile_active to work
COMPOSE_PROFILES="$COMPOSE_PROFILES_VALUE"
if is_profile_active "cloudflare-tunnel"; then
log_subheader "Cloudflare Tunnel"
existing_cf_token="$(read_env_var CLOUDFLARE_TUNNEL_TOKEN)"

View File

@@ -32,6 +32,23 @@ require_file "$PROJECT_ROOT/docker-compose.yml" "docker-compose.yml file not fou
require_file "$PROJECT_ROOT/Caddyfile" "Caddyfile not found in project root. Reverse proxy might not work."
require_file "$PROJECT_ROOT/start_services.py" "start_services.py file not found in project root."
# Remove legacy custom-tls.conf that causes duplicate host errors
# This is needed for users upgrading from older versions
# TODO: Remove this cleanup block after v3.0 release (all users migrated)
OLD_TLS_CONFIG="$PROJECT_ROOT/caddy-addon/custom-tls.conf"
if [[ -f "$OLD_TLS_CONFIG" ]]; then
log_warning "Removing obsolete custom-tls.conf (causes duplicate host errors)"
rm -f "$OLD_TLS_CONFIG"
fi
# Ensure TLS snippet exists (auto-create from template if missing)
TLS_SNIPPET="$PROJECT_ROOT/caddy-addon/tls-snippet.conf"
TLS_TEMPLATE="$PROJECT_ROOT/caddy-addon/tls-snippet.conf.example"
if [[ ! -f "$TLS_SNIPPET" ]] && [[ -f "$TLS_TEMPLATE" ]]; then
cp "$TLS_TEMPLATE" "$TLS_SNIPPET"
log_info "Created tls-snippet.conf from template (Let's Encrypt mode)"
fi
# Check if Docker daemon is running
if ! docker info > /dev/null 2>&1; then
log_error "Docker daemon is not running. Please start Docker and try again."

View File

@@ -79,6 +79,9 @@ echo ""
echo -e " ${WHITE}2.${NC} Store the Welcome Page credentials securely"
echo ""
echo -e " ${WHITE}3.${NC} Configure services as needed:"
if is_profile_active "appsmith"; then
echo -e " ${GREEN}*${NC} ${WHITE}Appsmith${NC}: Create admin account on first login (may take a few minutes to start)"
fi
if is_profile_active "n8n"; then
echo -e " ${GREEN}*${NC} ${WHITE}n8n${NC}: Complete first-run setup with your email"
fi

View File

@@ -27,6 +27,19 @@ GENERATED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
# Build services array - each entry is a formatted JSON block
declare -a SERVICES_ARRAY
# Appsmith
if is_profile_active "appsmith"; then
SERVICES_ARRAY+=(" \"appsmith\": {
\"hostname\": \"$(json_escape "$APPSMITH_HOSTNAME")\",
\"credentials\": {
\"note\": \"Create your account on first login\"
},
\"extra\": {
\"docs\": \"https://docs.appsmith.com\"
}
}")
fi
# n8n
if is_profile_active "n8n"; then
N8N_WORKER_COUNT_VAL="${N8N_WORKER_COUNT:-1}"
@@ -519,6 +532,16 @@ if is_profile_active "databasus"; then
((STEP_NUM++))
fi
# Set up Appsmith (if appsmith active)
if is_profile_active "appsmith"; then
QUICK_START_ARRAY+=(" {
\"step\": $STEP_NUM,
\"title\": \"Set up Appsmith\",
\"description\": \"Create your admin account and build your first app\"
}")
((STEP_NUM++))
fi
# Step 4: Monitor system (if monitoring active)
if is_profile_active "monitoring"; then
QUICK_START_ARRAY+=(" {

View File

@@ -2,12 +2,13 @@
# =============================================================================
# setup_custom_tls.sh - Configure custom TLS certificates for Caddy
# =============================================================================
# Generates caddy-addon/custom-tls.conf for using corporate/internal certificates
# Updates caddy-addon/tls-snippet.conf to use corporate/internal certificates
# instead of Let's Encrypt.
#
# Usage:
# bash scripts/setup_custom_tls.sh # Interactive mode
# bash scripts/setup_custom_tls.sh cert.crt key.key # Non-interactive mode
# bash scripts/setup_custom_tls.sh --remove # Reset to Let's Encrypt
#
# Prerequisites:
# - Place certificate files in ./certs/ directory
@@ -18,13 +19,27 @@ set -euo pipefail
source "$(dirname "$0")/utils.sh" && init_paths
ADDON_FILE="$PROJECT_ROOT/caddy-addon/custom-tls.conf"
SNIPPET_FILE="$PROJECT_ROOT/caddy-addon/tls-snippet.conf"
SNIPPET_EXAMPLE="$PROJECT_ROOT/caddy-addon/tls-snippet.conf.example"
CERTS_DIR="$PROJECT_ROOT/certs"
# Legacy file that causes duplicate host errors (must be cleaned up on migration)
# TODO: Remove OLD_CONFIG and cleanup_legacy_config() after v3.0 release (all users migrated)
OLD_CONFIG="$PROJECT_ROOT/caddy-addon/custom-tls.conf"
# =============================================================================
# FUNCTIONS
# =============================================================================
cleanup_legacy_config() {
# Remove old custom-tls.conf that causes duplicate host errors
# This is needed for users upgrading from older versions
if [[ -f "$OLD_CONFIG" ]]; then
log_warning "Removing obsolete custom-tls.conf (causes duplicate host errors)"
rm -f "$OLD_CONFIG"
fi
}
show_help() {
cat << EOF
Setup Custom TLS Certificates for Caddy
@@ -33,7 +48,7 @@ Usage: $(basename "$0") [OPTIONS] [CERT_FILE] [KEY_FILE]
Options:
-h, --help Show this help message
--remove Remove custom TLS configuration
--remove Reset to Let's Encrypt automatic certificates
Arguments:
CERT_FILE Path to certificate file (relative to ./certs/)
@@ -42,13 +57,12 @@ Arguments:
Examples:
$(basename "$0") # Interactive mode
$(basename "$0") wildcard.crt wildcard.key # Use specific files
$(basename "$0") --remove # Remove custom TLS config
$(basename "$0") --remove # Reset to Let's Encrypt
The script will:
1. Detect certificate files in ./certs/
2. Read active services from .env
3. Generate caddy-addon/custom-tls.conf
4. Optionally restart Caddy
2. Update caddy-addon/tls-snippet.conf with your certificate paths
3. Optionally restart Caddy
EOF
}
@@ -75,157 +89,53 @@ find_keys() {
echo "${keys[*]:-}"
}
get_active_services() {
# Get list of services with their hostnames from .env
load_env
local services=()
# Map of service names to their hostname variables
declare -A service_map=(
["n8n"]="N8N_HOSTNAME"
["flowise"]="FLOWISE_HOSTNAME"
["webui"]="WEBUI_HOSTNAME"
["grafana"]="GRAFANA_HOSTNAME"
["prometheus"]="PROMETHEUS_HOSTNAME"
["portainer"]="PORTAINER_HOSTNAME"
["langfuse"]="LANGFUSE_HOSTNAME"
["supabase"]="SUPABASE_HOSTNAME"
["dify"]="DIFY_HOSTNAME"
["nocodb"]="NOCODB_HOSTNAME"
["ragapp"]="RAGAPP_HOSTNAME"
["ragflow"]="RAGFLOW_HOSTNAME"
["waha"]="WAHA_HOSTNAME"
["searxng"]="SEARXNG_HOSTNAME"
["comfyui"]="COMFYUI_HOSTNAME"
["welcome"]="WELCOME_HOSTNAME"
["databasus"]="DATABASUS_HOSTNAME"
["letta"]="LETTA_HOSTNAME"
["lightrag"]="LIGHTRAG_HOSTNAME"
["weaviate"]="WEAVIATE_HOSTNAME"
["qdrant"]="QDRANT_HOSTNAME"
["neo4j"]="NEO4J_HOSTNAME"
["postiz"]="POSTIZ_HOSTNAME"
["libretranslate"]="LT_HOSTNAME"
["paddleocr"]="PADDLEOCR_HOSTNAME"
["docling"]="DOCLING_HOSTNAME"
)
for service in "${!service_map[@]}"; do
local hostname_var="${service_map[$service]}"
local hostname="${!hostname_var:-}"
if [[ -n "$hostname" && "$hostname" != *"yourdomain.com" ]]; then
services+=("$service:$hostname")
ensure_snippet_exists() {
# Create tls-snippet.conf from example if it doesn't exist
# This ensures the file survives git updates (it's gitignored)
if [[ ! -f "$SNIPPET_FILE" ]]; then
if [[ -f "$SNIPPET_EXAMPLE" ]]; then
cp "$SNIPPET_EXAMPLE" "$SNIPPET_FILE"
log_info "Created tls-snippet.conf from template"
else
# Fallback: create default content directly
remove_config
fi
done
echo "${services[*]:-}"
fi
}
generate_config() {
local cert_file="$1"
local key_file="$2"
local services=("${@:3}")
cat > "$ADDON_FILE" << 'HEADER'
# Custom TLS Configuration
# Generated by setup_custom_tls.sh
#
# This file overrides default Let's Encrypt certificates with custom ones.
# Regenerate with: make setup-tls
cat > "$SNIPPET_FILE" << EOF
# TLS Configuration Snippet
# Generated by setup_custom_tls.sh on $(date -Iseconds)
# Using custom certificates instead of Let's Encrypt.
# Reset to Let's Encrypt: make setup-tls --remove
# Reusable TLS snippet
(custom_tls) {
HEADER
(service_tls) {
tls /etc/caddy/certs/$cert_file /etc/caddy/certs/$key_file
}
EOF
echo " tls /etc/caddy/certs/$cert_file /etc/caddy/certs/$key_file" >> "$ADDON_FILE"
echo "}" >> "$ADDON_FILE"
echo "" >> "$ADDON_FILE"
# Service-specific reverse proxy mappings
declare -A proxy_map=(
["n8n"]="n8n:5678"
["flowise"]="flowise:3001"
["webui"]="open-webui:8080"
["grafana"]="grafana:3000"
["prometheus"]="prometheus:9090"
["portainer"]="portainer:9000"
["langfuse"]="langfuse-web:3000"
["supabase"]="kong:8000"
["dify"]="nginx:80"
["nocodb"]="nocodb:8080"
["ragapp"]="ragapp:8000"
["ragflow"]="ragflow:80"
["waha"]="waha:3000"
["searxng"]="searxng:8080"
["comfyui"]="comfyui:8188"
["welcome"]="file_server"
["databasus"]="databasus:4005"
["letta"]="letta:8283"
["lightrag"]="lightrag:9621"
["weaviate"]="weaviate:8080"
["qdrant"]="qdrant:6333"
["neo4j"]="neo4j:7474"
["postiz"]="postiz:5000"
["libretranslate"]="libretranslate:5000"
["paddleocr"]="paddleocr:8080"
["docling"]="docling:5001"
)
# Services that need basic auth (format: USERNAME_VAR:PASSWORD_HASH_VAR)
declare -A auth_services=(
["prometheus"]="PROMETHEUS_USERNAME:PROMETHEUS_PASSWORD_HASH"
["ragapp"]="RAGAPP_USERNAME:RAGAPP_PASSWORD_HASH"
["comfyui"]="COMFYUI_USERNAME:COMFYUI_PASSWORD_HASH"
["welcome"]="WELCOME_USERNAME:WELCOME_PASSWORD_HASH"
["libretranslate"]="LT_USERNAME:LT_PASSWORD_HASH"
["paddleocr"]="PADDLEOCR_USERNAME:PADDLEOCR_PASSWORD_HASH"
["docling"]="DOCLING_USERNAME:DOCLING_PASSWORD_HASH"
)
for service_entry in "${services[@]}"; do
local service="${service_entry%%:*}"
local hostname="${service_entry#*:}"
local proxy="${proxy_map[$service]:-}"
[[ -z "$proxy" ]] && continue
echo "# $service" >> "$ADDON_FILE"
echo "$hostname {" >> "$ADDON_FILE"
echo " import custom_tls" >> "$ADDON_FILE"
# Add basic auth if needed
if [[ -n "${auth_services[$service]:-}" ]]; then
local auth_config="${auth_services[$service]}"
local username_var="${auth_config%%:*}"
local password_hash_var="${auth_config#*:}"
echo " basic_auth {" >> "$ADDON_FILE"
echo " {\$${username_var}} {\$${password_hash_var}}" >> "$ADDON_FILE"
echo " }" >> "$ADDON_FILE"
fi
# Add reverse proxy or file server
if [[ "$proxy" == "file_server" ]]; then
echo " root * /srv/welcome" >> "$ADDON_FILE"
echo " file_server" >> "$ADDON_FILE"
echo " try_files {path} /index.html" >> "$ADDON_FILE"
else
echo " reverse_proxy $proxy" >> "$ADDON_FILE"
fi
echo "}" >> "$ADDON_FILE"
echo "" >> "$ADDON_FILE"
done
log_success "Generated $ADDON_FILE"
log_success "Generated $SNIPPET_FILE"
}
remove_config() {
if [[ -f "$ADDON_FILE" ]]; then
rm -f "$ADDON_FILE"
log_success "Removed custom TLS configuration"
else
log_info "No custom TLS configuration found"
fi
cat > "$SNIPPET_FILE" << 'EOF'
# TLS Configuration Snippet
# Imported by all service blocks in the main Caddyfile.
#
# Default: Empty (uses Let's Encrypt automatic certificates)
# Custom: Overwritten by 'make setup-tls' with your certificate paths
# Reset: Run 'make setup-tls --remove' to restore Let's Encrypt
(service_tls) {
# Default: Let's Encrypt automatic certificates (empty = no override)
}
EOF
log_success "Reset to Let's Encrypt (automatic certificates)"
}
restart_caddy() {
@@ -250,12 +160,19 @@ main() {
exit 0
;;
--remove)
cleanup_legacy_config
remove_config
restart_caddy
exit 0
;;
esac
# Clean up legacy config that causes duplicate hosts
cleanup_legacy_config
# Ensure snippet file exists (survives git updates)
ensure_snippet_exists
# Ensure certs directory exists
mkdir -p "$CERTS_DIR"
@@ -319,29 +236,16 @@ main() {
log_info "Using certificate: $cert_file"
log_info "Using key: $key_file"
# Get active services
local services_arr
IFS=' ' read -ra services_arr <<< "$(get_active_services)"
if [[ ${#services_arr[@]} -eq 0 ]]; then
log_warning "No services with configured hostnames found in .env"
log_info "Make sure to update *_HOSTNAME variables in .env with your domain"
exit 1
fi
log_info "Found ${#services_arr[@]} services with configured hostnames"
# Ensure certificate files are readable by Caddy container
# (Docker volume mounts preserve host permissions, Caddy may run as different UID)
chmod 644 "$CERTS_DIR/$cert_file" "$CERTS_DIR/$key_file"
# Generate configuration
generate_config "$cert_file" "$key_file" "${services_arr[@]}"
generate_config "$cert_file" "$key_file"
# Show summary
echo ""
log_info "Configuration generated for the following services:"
for service_entry in "${services_arr[@]}"; do
local service="${service_entry%%:*}"
local hostname="${service_entry#*:}"
echo " - $service: $hostname"
done
log_info "Custom TLS configured successfully!"
log_info "All services will use: /etc/caddy/certs/$cert_file"
echo ""
# Restart Caddy

View File

@@ -134,6 +134,11 @@ if is_profile_active "databasus"; then
check_image_update "databasus" "databasus/databasus:latest"
fi
if is_profile_active "appsmith"; then
log_subheader "Appsmith"
check_image_update "appsmith" "appsmith/appsmith-ce:release"
fi
# Summary
log_divider
echo ""

View File

@@ -148,6 +148,14 @@
// DATA - Service metadata and commands
// ============================================
const SERVICE_METADATA = {
'appsmith': {
name: 'Appsmith',
description: 'Low-code Internal Tools',
icon: 'AS',
color: 'bg-[#5f2dde]',
category: 'tools',
docsUrl: 'https://docs.appsmith.com'
},
'n8n': {
name: 'n8n',
description: 'Workflow Automation',