32 Commits

Author SHA1 Message Date
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
Yury Kossakovsky
a99676e3d5 fix(postiz): improve temporal integration
- increase elasticsearch memory to 512mb
- add temporal databases to initialization
- add postiz to final report
2026-01-17 19:56:29 -07:00
Yury Kossakovsky
bf7ce20f7b fix(caddy): add http block for welcome page to prevent redirect loop
when accessing welcome page through cloudflare tunnel, caddy was
redirecting http to https, causing an infinite redirect loop.
adding an explicit http block prevents automatic https redirect.
2026-01-17 19:42:50 -07:00
Yury Kossakovsky
36717a45c9 docs(readme): clarify vps requirement in prerequisites 2026-01-17 12:28:55 -07:00
Yury Kossakovsky
31b81b71a4 fix(postiz): add elasticsearch for temporal advanced visibility
temporal with sql visibility has a hard limit of 3 text search
attributes per namespace. postiz requires more, causing startup
failure. adding elasticsearch enables advanced visibility mode
which removes this limitation.
2026-01-17 12:26:40 -07:00
Yury Kossakovsky
a3e8f26925 fix(postiz): use correct temporal address env var 2026-01-16 20:27:06 -07:00
Yury Kossakovsky
917afe615c fix(temporal): use container ip for healthcheck connection 2026-01-16 20:15:03 -07:00
Yury Kossakovsky
641fd04290 fix(temporal): update healthcheck to use modern cli 2026-01-16 18:59:37 -07:00
Yury Kossakovsky
ca43e7ab12 docs(readme): add troubleshooting for update script issues 2026-01-16 18:48:31 -07:00
Yury Kossakovsky
e5db00098a refactor(docker-compose): extract logging config into yaml anchor 2026-01-16 18:45:30 -07:00
Yury Kossakovsky
4a6f1c0e01 feat(postiz): add temporal server for workflow orchestration
add temporal and temporal-ui services to the postiz profile for
workflow orchestration. includes caddy reverse proxy with basic
auth, secret generation, and welcome page integration.
2026-01-16 18:42:54 -07:00
Yury Kossakovsky
19cd6b6f91 docs(cloudflare): update tunnel instructions and add missing services
- update dashboard navigation to match current cloudflare ui
- add nocodb and welcome page to services table
- add notes explaining external compose files and caddy-served content
2026-01-13 08:40:36 -07:00
Yury Kossakovsky
b28093b5cd feat(welcome): add changelog section to dashboard 2026-01-12 10:03:46 -07:00
Yury Kossakovsky
361a726a07 docs(changelog): update v1.1.0 release date 2026-01-11 13:10:32 -07:00
Yury Kossakovsky
0b4c9d5dda feat(makefile): add stop and start commands for service control 2026-01-10 11:02:23 -07:00
Yury Kossakovsky
0e4b46ec31 feat(tls): add custom tls certificate support for corporate deployments
adds caddy-addon mechanism for custom certificates when let's encrypt
is not available. includes setup script with interactive wizard,
example configs, and documentation.
2026-01-09 23:26:41 -07:00
Yury Kossakovsky
e8f7934224 feat(git): add make git-pull command for fork workflows
adds support for forks that maintain custom changes and need to merge
from upstream instead of resetting. the new GIT_MODE=merge option in
update.sh calls git_merge_from_upstream() which fetches and merges
upstream/main while preserving local commits.
2026-01-09 20:28:22 -07:00
28 changed files with 1028 additions and 108 deletions

View File

@@ -314,14 +314,16 @@ ${SERVICE_NAME_UPPER}_PASSWORD=
${SERVICE_NAME_UPPER}_PASSWORD_HASH=
```
### 3.3 GOST_NO_PROXY (if using proxy-env)
### 3.3 GOST_NO_PROXY (REQUIRED for ALL services)
Add service to comma-separated list:
**CRITICAL:** Add ALL new service container names to the comma-separated list to prevent internal Docker traffic from going through the proxy:
```dotenv
GOST_NO_PROXY=localhost,127.0.0.1,...existing...,$ARGUMENTS
```
This applies to ALL services, not just those using `<<: *proxy-env`. Internal service-to-service communication must bypass the proxy.
---
## STEP 4: scripts/03_generate_secrets.sh
@@ -706,6 +708,7 @@ bash -n scripts/07_final_report.sh
- [ ] `docker-compose.yml`: caddy environment vars (if external)
- [ ] `Caddyfile`: reverse proxy block (if external)
- [ ] `.env.example`: hostname added
- [ ] `.env.example`: service added to `GOST_NO_PROXY` (ALL internal services must be listed)
- [ ] `scripts/03_generate_secrets.sh`: password in `VARS_TO_GENERATE`
- [ ] `scripts/04_wizard.sh`: service in `base_services_data`
- [ ] `scripts/generate_welcome_page.sh`: `SERVICES_ARRAY` entry
@@ -722,7 +725,6 @@ bash -n scripts/07_final_report.sh
### If Outbound Proxy (AI API calls)
- [ ] `docker-compose.yml`: `<<: *proxy-env` in environment
- [ ] `.env.example`: service added to `GOST_NO_PROXY`
- [ ] `docker-compose.yml`: healthcheck bypasses proxy
### If Database Required

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
@@ -164,6 +174,7 @@ NOCODB_HOSTNAME=nocodb.yourdomain.com
PADDLEOCR_HOSTNAME=paddleocr.yourdomain.com
PORTAINER_HOSTNAME=portainer.yourdomain.com
POSTIZ_HOSTNAME=postiz.yourdomain.com
TEMPORAL_UI_HOSTNAME=temporal.yourdomain.com
PROMETHEUS_HOSTNAME=prometheus.yourdomain.com
QDRANT_HOSTNAME=qdrant.yourdomain.com
RAGAPP_HOSTNAME=ragapp.yourdomain.com
@@ -429,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,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
@@ -474,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=
@@ -489,6 +510,13 @@ RAGAPP_PASSWORD_HASH=
POSTIZ_DISABLE_REGISTRATION=false
############
# Temporal UI credentials (for Caddy basic auth)
############
TEMPORAL_UI_USERNAME=
TEMPORAL_UI_PASSWORD=
TEMPORAL_UI_PASSWORD_HASH=
############
# Postiz Social Media Integrations
# Leave blank if not used. Provide credentials from each platform.

11
.gitignore vendored
View File

@@ -11,4 +11,13 @@ dify/
volumes/
docker-compose.override.yml
docker-compose.n8n-workers.yml
welcome/data.json
welcome/data.json
welcome/changelog.json
# Custom TLS certificates
certs/*
!certs/.gitkeep
# Custom Caddy addons (user configurations)
caddy-addon/*.conf
!caddy-addon/*.example

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

@@ -1,12 +1,65 @@
# Changelog
All notable changes to this project are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [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
- **Temporal** - Temporal server and UI for Postiz workflow orchestration (#33)
## [1.2.0] - 2026-01-12
### Added
- Changelog section on Welcome Page dashboard
## [1.1.0] - 2026-01-11
### Added
- **Custom TLS certificates** - Support for corporate/internal certificates via `caddy-addon/` mechanism
- New `make stop` and `make start` commands for stopping/starting all services without restart
- New `make setup-tls` command and `scripts/setup_custom_tls.sh` helper script for easy certificate configuration
- New `make git-pull` command for fork workflows - merges from upstream instead of hard reset
## [1.0.0] - 2026-01-07
### Added
@@ -211,3 +264,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Langfuse - LLM observability and analytics platform
- Initial fork from coleam00/local-ai-packager with enhanced service support
---
All notable changes to this project are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

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,14 +62,17 @@ 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
```bash
make install # Full installation (runs scripts/install.sh)
make update # Update system and services
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)
@@ -74,10 +81,13 @@ 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
@@ -152,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
@@ -166,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
@@ -272,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,41 +88,58 @@
# 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}
}
reverse_proxy temporal-ui:8080
}
# 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}
}
@@ -116,6 +148,7 @@
# LibreTranslate (Self-hosted Translation API)
{$LT_HOSTNAME} {
import service_tls
basic_auth {
{$LT_USERNAME} {$LT_PASSWORD_HASH}
}
@@ -124,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}
}
@@ -147,6 +184,7 @@ https://{$NEO4J_HOSTNAME}:7687 {
# Docling (Document Conversion API)
{$DOCLING_HOSTNAME} {
import service_tls
basic_auth {
{$DOCLING_USERNAME} {$DOCLING_PASSWORD_HASH}
}
@@ -154,7 +192,8 @@ https://{$NEO4J_HOSTNAME}:7687 {
}
# Welcome Page (Post-install dashboard)
{$WELCOME_HOSTNAME} {
# HTTP block for Cloudflare Tunnel access (prevents redirect loop)
http://{$WELCOME_HOSTNAME} {
basic_auth {
{$WELCOME_USERNAME} {$WELCOME_PASSWORD_HASH}
}
@@ -163,10 +202,23 @@ https://{$NEO4J_HOSTNAME}:7687 {
try_files {path} /index.html
}
import /etc/caddy/addons/*.conf
# HTTPS block for direct access
{$WELCOME_HOSTNAME} {
import service_tls
basic_auth {
{$WELCOME_USERNAME} {$WELCOME_PASSWORD_HASH}
}
root * /srv/welcome
file_server
try_files {path} /index.html
}
# # SearXNG
# Import custom site addons
import /etc/caddy/addons/site-*.conf
# 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

@@ -1,4 +1,4 @@
.PHONY: help install update update-preview clean clean-all logs status monitor restart show-restarts doctor switch-beta switch-stable import
.PHONY: help install update update-preview git-pull clean clean-all logs status monitor restart stop start show-restarts doctor switch-beta switch-stable import setup-tls
PROJECT_NAME := localai
@@ -6,8 +6,9 @@ help:
@echo "n8n-install - Available commands:"
@echo ""
@echo " make install Full installation"
@echo " make update Update system and services"
@echo " make update Update system and services (resets to origin)"
@echo " make update-preview Preview available updates (dry-run)"
@echo " make git-pull Update for forks (merges from upstream)"
@echo " make clean Remove unused Docker resources (preserves data)"
@echo " make clean-all Remove ALL Docker resources including data (DANGEROUS)"
@echo ""
@@ -16,10 +17,13 @@ help:
@echo " make status Show container status"
@echo " make monitor Live CPU/memory monitoring"
@echo " make restart Restart all services"
@echo " make stop Stop all services"
@echo " make start Start all services"
@echo " make show-restarts Show restart count per container"
@echo " make doctor Run system diagnostics"
@echo " make import Import n8n workflows from backup"
@echo " make import n=10 Import first N workflows only"
@echo " make setup-tls Configure custom TLS certificates"
@echo ""
@echo " make switch-beta Switch to beta (develop branch)"
@echo " make switch-stable Switch to stable (main branch)"
@@ -33,6 +37,9 @@ update:
update-preview:
bash ./scripts/update_preview.sh
git-pull:
sudo GIT_MODE=merge bash ./scripts/update.sh
clean:
sudo bash ./scripts/docker_cleanup.sh
@@ -58,6 +65,12 @@ monitor:
restart:
bash ./scripts/restart.sh
stop:
docker compose -p $(PROJECT_NAME) stop
start:
docker compose -p $(PROJECT_NAME) start
show-restarts:
@docker ps -q | while read id; do \
name=$$(docker inspect --format '{{.Name}}' $$id | sed 's/^\/\(.*\)/\1/'); \
@@ -84,3 +97,6 @@ ifdef n
else
docker compose -p $(PROJECT_NAME) run --rm -e FORCE_IMPORT=true n8n-import
endif
setup-tls:
bash ./scripts/setup_custom_tls.sh

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.
@@ -137,9 +139,10 @@ Get started quickly with a vast library of pre-built automations (optional impor
1. **Domain Name:** You need a registered domain name (e.g., `yourdomain.com`).
2. **DNS Configuration:** Before running the installation script, you **must** configure DNS A-record for your domain, pointing to the public IP address of the server where you'll install this system. Replace `yourdomain.com` with your actual domain:
- **Wildcard Record:** `A *.yourdomain.com` -> `YOUR_SERVER_IP`
3. **Server:** Minimum server system requirements: Ubuntu 24.04 LTS, 64-bit.
- For running **all available services**: at least **20 GB Memory / 4 CPU Cores / 60 GB Disk Space**.
- For a minimal setup with **n8n, Monitoring, Databasus and Portainer**: **4 GB Memory / 2 CPU Cores / 40 GB Disk Space**.
3. **VPS (Virtual Private Server):** A dedicated VPS with a public IP address is required. Home servers, shared hosting, or localhost setups are not supported.
- **Operating System:** Ubuntu 24.04 LTS, 64-bit
- For a minimal setup with **n8n, Monitoring, Databasus and Portainer**: **4 GB Memory / 2 CPU Cores / 40 GB Disk Space**
- For running **all available services**: at least **20 GB Memory / 4 CPU Cores / 60 GB Disk Space**
### Running the Install
@@ -178,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)
@@ -271,6 +275,12 @@ To update all components (n8n, Open WebUI, etc.) to their latest versions and in
make update
```
**For forks**: If you maintain a fork with custom changes and want to merge updates from upstream instead of resetting:
```bash
make git-pull
```
This script will:
1. Fetch the latest updates for the installer from the Git repository.
@@ -298,8 +308,9 @@ The project includes a Makefile for simplified command execution:
| Command | Description |
| --------------------- | ---------------------------------------------------- |
| `make install` | Full installation |
| `make update` | Update system and services |
| `make update` | Update system and services (resets to origin) |
| `make update-preview` | Preview available updates without applying (dry-run) |
| `make git-pull` | Update for forks (merges from upstream/main) |
| `make clean` | Remove unused Docker resources |
### Monitoring & Logs
@@ -311,15 +322,18 @@ The project includes a Makefile for simplified command execution:
| `make status` | Show container status |
| `make monitor` | Live CPU/memory monitoring |
| `make restart` | Restart all services |
| `make stop` | Stop all services |
| `make start` | Start all services |
| `make show-restarts` | Show restart count per container |
| `make import` | Import n8n workflows from backup |
| `make import n=10` | Import first N workflows only |
### Diagnostics
### Diagnostics & Configuration
| Command | Description |
| ------------- | ------------------------------------------------------------------ |
| `make doctor` | Run system diagnostics (checks DNS, SSL, containers, disk, memory) |
| Command | Description |
| ---------------- | ------------------------------------------------------------------ |
| `make doctor` | Run system diagnostics (checks DNS, SSL, containers, disk, memory) |
| `make setup-tls` | Configure custom TLS certificates for corporate/internal use |
Run `make help` for the full list of available commands.
@@ -357,6 +371,18 @@ Here are solutions to common issues you might encounter:
- **VPN Conflicts:** Using a VPN might interfere with downloading Docker images. If you encounter issues pulling images, try temporarily disabling your VPN.
- **Server Requirements:** If you experience unexpected issues, ensure your server meets the minimum hardware and operating system requirements (including version) as specified in the "Prerequisites before Installation" section.
### Update Script Not Working
- **Symptom:** The `make update` command fails, shows errors, or doesn't apply the latest changes.
- **Cause:** This can happen if your local repository has diverged from the upstream, has uncommitted changes, or is in an inconsistent state.
- **Solution:** Run the following command to force-sync your local installation with the latest version:
```bash
git config pull.rebase true && git fetch origin && git checkout main && git reset --hard "origin/main" && make update
```
**Warning:** This will discard any local changes you've made to the installer files. If you've customized any scripts or configurations, back them up first.
## Recommended Reading
n8n offers excellent resources for getting started with its AI capabilities:

View File

@@ -1 +1 @@
1.0.0
1.3.0

110
caddy-addon/README.md Normal file
View File

@@ -0,0 +1,110 @@
# Caddy Addons
This directory allows you to extend or override Caddy configuration without modifying 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
- Custom TLS certificates (corporate/internal CA)
- Additional reverse proxy rules
- Custom headers or middleware
- Rate limiting or access control
## Custom TLS Certificates
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:
```bash
cp /path/to/your/cert.crt ./certs/wildcard.crt
cp /path/to/your/key.key ./certs/wildcard.key
```
2. Run the setup script:
```bash
make setup-tls
```
3. The script will:
- Update `caddy-addon/tls-snippet.conf` with your certificate paths
- Optionally restart Caddy to apply changes
### Reset to Let's Encrypt
To switch back to automatic Let's Encrypt certificates:
```bash
make setup-tls --remove
```
Or run directly:
```bash
bash scripts/setup_custom_tls.sh --remove
```
## File Structure
```
caddy-addon/
├── .gitkeep # Keeps directory in git
├── README.md # This file
├── 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
├── wildcard.crt # Your certificate (gitignored)
└── 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
- `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)
- Caddy validates configuration on startup - check logs if it fails:
```bash
docker compose -p localai logs caddy
```
## Caddy Documentation
- [Caddyfile Syntax](https://caddyserver.com/docs/caddyfile)
- [TLS Directive](https://caddyserver.com/docs/caddyfile/directives/tls)
- [Reverse Proxy](https://caddyserver.com/docs/caddyfile/directives/reverse_proxy)

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

0
certs/.gitkeep Normal file
View File

View File

@@ -22,8 +22,8 @@ Cloudflare Tunnel **bypasses Caddy** and connects directly to your services. Thi
1. Go to [Cloudflare One Dashboard](https://one.dash.cloudflare.com/)
2. Navigate to **Networks****Connectors****Cloudflare Tunnels**
3. Click **Create new cloudflared Tunnel**
4. Choose **Cloudflared** connector and click **Next**
3. Click **Create a tunnel**
4. Select **Cloudflared** as the connector type and click **Next**
5. Name your tunnel (e.g., "n8n-install") and click **Save tunnel**
6. Copy the installation command shown - it contains your tunnel token
@@ -106,7 +106,7 @@ dig NS yourdomain.com +short
#### 3. Configure Public Hostnames
After DNS is configured, go to **Cloudflare Zero Trust** → **Networks** → **Tunnels** → your tunnel → **Public Hostname** tab. For each service you want to expose, click **Add a public hostname** and configure:
After DNS is configured, go to **Cloudflare One Dashboard** → **Networks** → **Connectors** → **Cloudflare Tunnels** → your tunnel → **Public Hostname** tab. For each service you want to expose, click **Add a public hostname** and configure:
| Service | Public Hostname | Service URL | Auth Notes |
| ------------------ | ----------------------------- | ---------------------------- | ------------------- |
@@ -122,6 +122,7 @@ After DNS is configured, go to **Cloudflare Zero Trust** → **Networks** → **
| **LibreTranslate** | libretranslate.yourdomain.com | `http://libretranslate:5000` | ⚠️ Loses Caddy auth |
| **LightRAG** | lightrag.yourdomain.com | `http://lightrag:9621` | No auth |
| **Neo4j** | neo4j.yourdomain.com | `http://neo4j:7474` | Built-in login |
| **NocoDB** | nocodb.yourdomain.com | `http://nocodb:8080` | Built-in login |
| **Open WebUI** | webui.yourdomain.com | `http://open-webui:8080` | Built-in login |
| **PaddleOCR** | paddleocr.yourdomain.com | `http://paddleocr:8080` | ⚠️ Loses Caddy auth |
| **Portainer** | portainer.yourdomain.com | `http://portainer:9000` | Built-in login |
@@ -134,6 +135,11 @@ After DNS is configured, go to **Cloudflare Zero Trust** → **Networks** → **
| **Supabase** ¹ | supabase.yourdomain.com | `http://kong:8000` | Built-in login |
| **WAHA** | waha.yourdomain.com | `http://waha:3000` | API key recommended |
| **Weaviate** | weaviate.yourdomain.com | `http://weaviate:8080` | API key recommended |
| **Welcome Page** ² | welcome.yourdomain.com | `http://caddy:80` | ⚠️ Loses Caddy auth |
**Notes:**
- ¹ Dify and Supabase use external compose files from adjacent directories
- ² Welcome Page is served by Caddy as static content; tunnel proxies through Caddy
**⚠️ Security Warning:**
- Services marked **"Loses Caddy auth"** have basic authentication via Caddy that is bypassed by the tunnel. Use [Cloudflare Access](https://developers.cloudflare.com/cloudflare-one/applications/) or keep them internal.
@@ -181,7 +187,7 @@ You have two options for accessing your services:
For services that lose Caddy's basic auth protection, you can add Cloudflare Access:
1. In **Cloudflare One Dashboard** → **Access controls** → **Applications**
1. In **Cloudflare One Dashboard** → **Access** → **Applications** (or **Access controls** → **Applications** depending on your dashboard version)
2. Click **Add an application** → **Self-hosted**
3. Configure:
- **Application name**: e.g., "Prometheus"

View File

@@ -1,4 +1,5 @@
volumes:
appsmith_data:
caddy-config:
caddy-data:
comfyui_data:
@@ -33,9 +34,17 @@ volumes:
ragflow_minio_data:
ragflow_mysql_data:
ragflow_redis_data:
temporal_elasticsearch_data:
valkey-data:
weaviate_data:
# Shared logging configuration for services
x-logging: &default-logging
driver: "json-file"
options:
max-size: "1m"
max-file: "1"
# Shared proxy configuration for services that need outbound proxy support
x-proxy-env: &proxy-env
HTTP_PROXY: ${GOST_PROXY_URL:-}
@@ -136,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
@@ -274,11 +303,7 @@ services:
container_name: nocodb
profiles: ["nocodb"]
restart: unless-stopped
logging:
driver: "json-file"
options:
max-size: "1m"
max-file: "1"
logging: *default-logging
environment:
NC_AUTH_JWT_SECRET: ${NOCODB_JWT_SECRET}
NC_DB: pg://postgres:5432?u=postgres&p=${POSTGRES_PASSWORD}&d=nocodb
@@ -309,10 +334,12 @@ services:
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ./caddy-addon:/etc/caddy/addons:ro
- ./certs:/etc/caddy/certs:ro
- ./welcome:/srv/welcome:ro
- 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}
@@ -338,6 +365,9 @@ services:
PORTAINER_HOSTNAME: ${PORTAINER_HOSTNAME}
DATABASUS_HOSTNAME: ${DATABASUS_HOSTNAME}
POSTIZ_HOSTNAME: ${POSTIZ_HOSTNAME}
TEMPORAL_UI_HOSTNAME: ${TEMPORAL_UI_HOSTNAME}
TEMPORAL_UI_USERNAME: ${TEMPORAL_UI_USERNAME}
TEMPORAL_UI_PASSWORD_HASH: ${TEMPORAL_UI_PASSWORD_HASH}
PROMETHEUS_HOSTNAME: ${PROMETHEUS_HOSTNAME}
PROMETHEUS_PASSWORD_HASH: ${PROMETHEUS_PASSWORD_HASH}
PROMETHEUS_USERNAME: ${PROMETHEUS_USERNAME}
@@ -360,11 +390,7 @@ services:
- ALL
cap_add:
- NET_BIND_SERVICE
logging:
driver: "json-file"
options:
max-size: "1m"
max-file: "1"
logging: *default-logging
cloudflared:
image: cloudflare/cloudflared:latest
@@ -374,11 +400,7 @@ services:
command: tunnel --no-autoupdate run
environment:
TUNNEL_TOKEN: ${CLOUDFLARE_TUNNEL_TOKEN}
logging:
driver: "json-file"
options:
max-size: "1m"
max-file: "1"
logging: *default-logging
gost:
image: gogost/gost:latest
@@ -396,11 +418,7 @@ services:
timeout: 10s
retries: 3
start_period: 10s
logging:
driver: "json-file"
options:
max-size: "1m"
max-file: "1"
logging: *default-logging
langfuse-worker:
image: langfuse/langfuse-worker:3
@@ -552,11 +570,7 @@ services:
- SETGID
- SETUID
- DAC_OVERRIDE
logging:
driver: "json-file"
options:
max-size: "1m"
max-file: "1"
logging: *default-logging
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 3s
@@ -579,11 +593,7 @@ services:
- CHOWN
- SETGID
- SETUID
logging:
driver: "json-file"
options:
max-size: "1m"
max-file: "1"
logging: *default-logging
ollama-cpu:
profiles: ["cpu"]
@@ -777,6 +787,70 @@ services:
- portainer_data:/data
- ${DOCKER_SOCKET_LOCATION:-/var/run/docker.sock}:/var/run/docker.sock
temporal-elasticsearch:
image: elasticsearch:7.17.27
container_name: temporal-elasticsearch
profiles: ["postiz"]
restart: unless-stopped
logging: *default-logging
environment:
cluster.routing.allocation.disk.threshold_enabled: "true"
cluster.routing.allocation.disk.watermark.low: 512mb
cluster.routing.allocation.disk.watermark.high: 256mb
cluster.routing.allocation.disk.watermark.flood_stage: 128mb
discovery.type: single-node
ES_JAVA_OPTS: -Xms512m -Xmx512m
xpack.security.enabled: "false"
volumes:
- temporal_elasticsearch_data:/usr/share/elasticsearch/data
healthcheck:
test: ["CMD-SHELL", "curl -s http://localhost:9200/_cluster/health | grep -qE '\"status\":\"(green|yellow)\"'"]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s
temporal:
image: temporalio/auto-setup:latest
container_name: temporal
profiles: ["postiz"]
restart: unless-stopped
logging: *default-logging
environment:
DB: postgres12
POSTGRES_USER: postgres
POSTGRES_PWD: ${POSTGRES_PASSWORD}
POSTGRES_SEEDS: postgres
DB_PORT: 5432
TEMPORAL_NAMESPACE: default
ENABLE_ES: "true"
ES_SEEDS: temporal-elasticsearch
ES_VERSION: v7
depends_on:
postgres:
condition: service_healthy
temporal-elasticsearch:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "temporal operator cluster health --address $(hostname -i):7233 | grep -q SERVING || exit 1"]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s
temporal-ui:
image: temporalio/ui:latest
container_name: temporal-ui
profiles: ["postiz"]
restart: unless-stopped
logging: *default-logging
environment:
TEMPORAL_ADDRESS: temporal:7233
TEMPORAL_CORS_ORIGINS: http://localhost:3000
depends_on:
temporal:
condition: service_healthy
postiz:
image: ghcr.io/gitroomhq/postiz-app:latest
container_name: postiz
@@ -784,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}
@@ -795,6 +869,7 @@ services:
NEXT_PUBLIC_UPLOAD_DIRECTORY: "/uploads"
REDIS_URL: "redis://redis:6379"
STORAGE_PROVIDER: "local"
TEMPORAL_ADDRESS: temporal:7233
UPLOAD_DIRECTORY: "/uploads"
# Social Media API Settings
X_API_KEY: ${X_API_KEY}
@@ -836,17 +911,15 @@ services:
condition: service_healthy
redis:
condition: service_healthy
temporal:
condition: service_healthy
databasus:
image: databasus/databasus:latest
container_name: databasus
profiles: ["databasus"]
restart: unless-stopped
logging:
driver: "json-file"
options:
max-size: "1m"
max-file: "1"
logging: *default-logging
volumes:
- databasus_data:/databasus-data
healthcheck:
@@ -857,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
@@ -979,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
@@ -1043,11 +1116,7 @@ services:
- SETGID
- SETUID
- DAC_OVERRIDE
logging:
driver: "json-file"
options:
max-size: "1m"
max-file: "1"
logging: *default-logging
healthcheck:
test: ["CMD", "valkey-cli", "-a", "${RAGFLOW_REDIS_PASSWORD}", "ping"]
interval: 3s

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

@@ -55,6 +55,7 @@ EMAIL_VARS=(
"PROMETHEUS_USERNAME"
"RAGAPP_USERNAME"
"SEARXNG_USERNAME"
"TEMPORAL_UI_USERNAME"
"WAHA_DASHBOARD_USERNAME"
"WEAVIATE_USERNAME"
"WELCOME_USERNAME"
@@ -73,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
@@ -114,6 +117,7 @@ declare -A VARS_TO_GENERATE=(
["RAGFLOW_REDIS_PASSWORD"]="password:32"
["SEARXNG_PASSWORD"]="password:32" # Added SearXNG admin password
["SECRET_KEY_BASE"]="base64:64" # 48 bytes -> 64 chars
["TEMPORAL_UI_PASSWORD"]="password:32" # Temporal UI basic auth password
["VAULT_ENC_KEY"]="alphanum:32"
["WAHA_DASHBOARD_PASSWORD"]="password:32"
["WEAVIATE_API_KEY"]="secret:48" # API Key for Weaviate service (36 bytes -> 48 chars base64)
@@ -564,7 +568,7 @@ if [[ -n "$template_no_proxy" ]]; then
fi
# Hash passwords using caddy with bcrypt (consolidated loop)
SERVICES_NEEDING_HASH=("PROMETHEUS" "SEARXNG" "COMFYUI" "PADDLEOCR" "RAGAPP" "LT" "DOCLING" "WELCOME")
SERVICES_NEEDING_HASH=("PROMETHEUS" "SEARXNG" "COMFYUI" "PADDLEOCR" "RAGAPP" "LT" "DOCLING" "TEMPORAL_UI" "WELCOME")
for service in "${SERVICES_NEEDING_HASH[@]}"; do
password_var="${service}_PASSWORD"

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

@@ -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
@@ -97,6 +100,9 @@ fi
if is_profile_active "nocodb"; then
echo -e " ${GREEN}*${NC} ${WHITE}NocoDB${NC}: Create your account on first login"
fi
if is_profile_active "postiz"; then
echo -e " ${GREEN}*${NC} ${WHITE}Postiz${NC}: Create your account on first login"
fi
if is_profile_active "gost"; then
echo -e " ${GREEN}*${NC} ${WHITE}Gost Proxy${NC}: Routing AI traffic through external proxy"
fi

View File

@@ -30,6 +30,8 @@ INIT_DB_DATABASES=(
"lightrag"
"nocodb"
"postiz"
"temporal"
"temporal_visibility"
"waha"
)

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}"
@@ -327,6 +340,20 @@ if is_profile_active "postiz"; then
}")
fi
# Temporal UI
if is_profile_active "postiz"; then
SERVICES_ARRAY+=(" \"temporal-ui\": {
\"hostname\": \"$(json_escape "$TEMPORAL_UI_HOSTNAME")\",
\"credentials\": {
\"username\": \"$(json_escape "$TEMPORAL_UI_USERNAME")\",
\"password\": \"$(json_escape "$TEMPORAL_UI_PASSWORD")\"
},
\"extra\": {
\"note\": \"Workflow orchestration admin for Postiz\"
}
}")
fi
# WAHA
if is_profile_active "waha"; then
SERVICES_ARRAY+=(" \"waha\": {
@@ -505,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+=(" {
@@ -541,3 +578,30 @@ EOF
log_success "Welcome page data generated at: $OUTPUT_FILE"
log_info "Access it at: https://${WELCOME_HOSTNAME:-welcome.${USER_DOMAIN_NAME}}"
# Generate changelog.json with CHANGELOG.md content
CHANGELOG_JSON_FILE="$PROJECT_ROOT/welcome/changelog.json"
CHANGELOG_SOURCE="$PROJECT_ROOT/CHANGELOG.md"
if [ -f "$CHANGELOG_SOURCE" ]; then
# Read and escape content for JSON (preserve newlines as \n)
# Using awk for cross-platform compatibility (macOS + Linux)
CHANGELOG_CONTENT=$(awk '
BEGIN { ORS="" }
{
gsub(/\\/, "\\\\") # Escape backslashes first
gsub(/"/, "\\\"") # Escape double quotes
gsub(/\t/, "\\t") # Escape tabs
gsub(/\r/, "") # Remove carriage returns (CRLF → LF)
if (NR > 1) printf "\\n"
printf "%s", $0
}
' "$CHANGELOG_SOURCE")
# Write changelog.json file
printf '{\n "content": "%s"\n}\n' "$CHANGELOG_CONTENT" > "$CHANGELOG_JSON_FILE"
log_success "Changelog JSON generated at: $CHANGELOG_JSON_FILE"
else
log_warning "CHANGELOG.md not found, skipping changelog.json generation"
fi

View File

@@ -71,7 +71,7 @@ git_get_current_branch() {
# SYNC OPERATIONS
#=============================================================================
# Sync local repository with origin
# Sync local repository with origin using hard reset
# Fetches latest changes and resets to origin/<branch>
# This discards any local commits to ensure clean sync with remote
# Usage: git_sync_with_origin [target_branch]
@@ -102,6 +102,40 @@ git_sync_with_origin() {
return 0
}
# Merge changes from upstream remote (for forks)
# Fetches from upstream and merges main branch into current branch
# Preserves local commits - suitable for fork workflows
# Usage: git_merge_from_upstream
# Returns: 0 on success, 1 on failure
git_merge_from_upstream() {
local upstream_branch="main"
# Check if upstream remote exists
if ! git remote get-url upstream &>/dev/null; then
log_error "Remote 'upstream' not configured."
log_info "Add it with: git remote add upstream <original-repo-url>"
return 1
fi
# Fetch latest changes from upstream
log_info "Fetching latest changes from upstream..."
if ! git fetch upstream; then
log_error "Git fetch from upstream failed. Check your internet connection."
return 1
fi
# Merge upstream changes into current branch
log_info "Merging upstream/$upstream_branch into current branch..."
if ! git merge "upstream/$upstream_branch" --no-edit; then
log_error "Git merge from upstream/$upstream_branch failed."
log_error "You may need to resolve conflicts manually."
return 1
fi
log_success "Successfully merged changes from upstream/$upstream_branch"
return 0
}
#=============================================================================
# CONFIGURATION
#=============================================================================

255
scripts/setup_custom_tls.sh Executable file
View File

@@ -0,0 +1,255 @@
#!/usr/bin/env bash
# =============================================================================
# setup_custom_tls.sh - Configure custom TLS certificates for Caddy
# =============================================================================
# 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
# - Certificate paths are relative to container (/etc/caddy/certs/)
# =============================================================================
set -euo pipefail
source "$(dirname "$0")/utils.sh" && init_paths
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
Usage: $(basename "$0") [OPTIONS] [CERT_FILE] [KEY_FILE]
Options:
-h, --help Show this help message
--remove Reset to Let's Encrypt automatic certificates
Arguments:
CERT_FILE Path to certificate file (relative to ./certs/)
KEY_FILE Path to private key file (relative to ./certs/)
Examples:
$(basename "$0") # Interactive mode
$(basename "$0") wildcard.crt wildcard.key # Use specific files
$(basename "$0") --remove # Reset to Let's Encrypt
The script will:
1. Detect certificate files in ./certs/
2. Update caddy-addon/tls-snippet.conf with your certificate paths
3. Optionally restart Caddy
EOF
}
find_certificates() {
# Find certificate files in certs directory
local certs=()
if [[ -d "$CERTS_DIR" ]]; then
while IFS= read -r -d '' file; do
certs+=("$(basename "$file")")
done < <(find "$CERTS_DIR" -maxdepth 1 -type f \( -name "*.crt" -o -name "*.pem" -o -name "*.cer" \) -print0 2>/dev/null)
fi
echo "${certs[*]:-}"
}
find_keys() {
# Find key files in certs directory
local keys=()
if [[ -d "$CERTS_DIR" ]]; then
while IFS= read -r -d '' file; do
keys+=("$(basename "$file")")
done < <(find "$CERTS_DIR" -maxdepth 1 -type f \( -name "*.key" -o -name "*-key.pem" \) -print0 2>/dev/null)
fi
echo "${keys[*]:-}"
}
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
fi
}
generate_config() {
local cert_file="$1"
local key_file="$2"
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
(service_tls) {
tls /etc/caddy/certs/$cert_file /etc/caddy/certs/$key_file
}
EOF
log_success "Generated $SNIPPET_FILE"
}
remove_config() {
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() {
if wt_yesno "Restart Caddy" "Do you want to restart Caddy to apply the new configuration?" "yes"; then
log_info "Restarting Caddy..."
docker compose -p localai restart caddy
log_success "Caddy restarted"
else
log_info "Skipped Caddy restart. Run manually: docker compose -p localai restart caddy"
fi
}
# =============================================================================
# MAIN
# =============================================================================
main() {
# Handle arguments
case "${1:-}" in
-h|--help)
show_help
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"
local cert_file=""
local key_file=""
# Non-interactive mode
if [[ $# -ge 2 ]]; then
cert_file="$1"
key_file="$2"
if [[ ! -f "$CERTS_DIR/$cert_file" ]]; then
log_error "Certificate not found: $CERTS_DIR/$cert_file"
exit 1
fi
if [[ ! -f "$CERTS_DIR/$key_file" ]]; then
log_error "Key not found: $CERTS_DIR/$key_file"
exit 1
fi
else
# Interactive mode
require_whiptail
# Find available certificates
local certs_arr
IFS=' ' read -ra certs_arr <<< "$(find_certificates)"
if [[ ${#certs_arr[@]} -eq 0 ]]; then
wt_msg "No Certificates Found" "No certificate files found in ./certs/\n\nPlease place your certificate (.crt, .pem, .cer) and key (.key) files in the certs/ directory first."
exit 1
fi
# Build menu items for certificates
local cert_items=()
for cert in "${certs_arr[@]}"; do
cert_items+=("$cert" "")
done
cert_file=$(wt_menu "Select Certificate" "Choose your TLS certificate file:" "${cert_items[@]}")
[[ -z "$cert_file" ]] && exit 1
# Find available keys
local keys_arr
IFS=' ' read -ra keys_arr <<< "$(find_keys)"
if [[ ${#keys_arr[@]} -eq 0 ]]; then
wt_msg "No Keys Found" "No key files found in ./certs/\n\nPlease place your private key (.key) file in the certs/ directory."
exit 1
fi
# Build menu items for keys
local key_items=()
for key in "${keys_arr[@]}"; do
key_items+=("$key" "")
done
key_file=$(wt_menu "Select Private Key" "Choose your TLS private key file:" "${key_items[@]}")
[[ -z "$key_file" ]] && exit 1
fi
log_info "Using certificate: $cert_file"
log_info "Using key: $key_file"
# 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"
echo ""
log_info "Custom TLS configured successfully!"
log_info "All services will use: /etc/caddy/certs/$cert_file"
echo ""
# Restart Caddy
restart_caddy
}
main "$@"

View File

@@ -4,7 +4,7 @@
# =============================================================================
# Performs a full system and service update:
# 1. Backs up user-customizable directories (e.g., python-runner/)
# 2. Fetches and resets to origin/<branch> (discards any local commits)
# 2. Syncs with remote repository (method depends on GIT_MODE)
# 3. Restores backed up directories to preserve user modifications
# 4. Updates Ubuntu system packages (apt-get update && upgrade)
# 5. Delegates to apply_update.sh for service updates
@@ -12,14 +12,19 @@
# This two-stage approach ensures apply_update.sh itself gets updated before
# running, so new update logic is always applied.
#
# Git strategy: We use `git fetch` + `git reset --hard origin/<branch>` instead
# of `git pull` to ensure we always sync with remote, even if the user has
# accidental local commits that would cause rebase conflicts.
# Git modes (set via GIT_MODE environment variable):
# - reset (default): git fetch + reset --hard origin/<branch>
# Best for: Standard installations, always syncs cleanly with remote
# - merge: git fetch upstream + merge upstream/<branch>
# Best for: Forks that maintain their own changes and merge from upstream
#
# Preserved directories: Defined in PRESERVE_DIRS array in utils.sh.
# These directories contain user-customizable content that survives git reset.
#
# Usage: make update OR sudo bash scripts/update.sh
# Usage:
# make update - Standard update (reset mode)
# make git-pull - Fork update (merge mode)
# GIT_MODE=merge sudo bash scripts/update.sh - Manual merge mode
# =============================================================================
set -e
@@ -85,10 +90,20 @@ if [ -n "$BACKUP_PATH" ]; then
log_info "Backup created at: $BACKUP_PATH"
fi
# Sync with origin (fetch + reset to remote branch)
if ! git_sync_with_origin; then
restore_preserved_dirs "$BACKUP_PATH"
exit 1
# Sync with remote repository based on GIT_MODE
if [[ "${GIT_MODE:-reset}" == "merge" ]]; then
# Fork workflow: merge from upstream (preserves local commits)
log_info "Using merge mode (for forks)..."
if ! git_merge_from_upstream; then
restore_preserved_dirs "$BACKUP_PATH"
exit 1
fi
else
# Standard workflow: reset to origin (discards local commits)
if ! git_sync_with_origin; then
restore_preserved_dirs "$BACKUP_PATH"
exit 1
fi
fi
# Restore user-customizable directories after git reset

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

@@ -136,6 +136,11 @@
warning: (className = '') => `
<svg class="${className}" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
</svg>`,
changelog: (className = '') => `
<svg class="${className}" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01"/>
</svg>`
};
@@ -143,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',
@@ -335,6 +348,14 @@
category: 'tools',
docsUrl: 'https://docs.postiz.com'
},
'temporal-ui': {
name: 'Temporal UI',
description: 'Postiz Workflow Orchestration',
icon: 'TM',
color: 'bg-violet-500',
category: 'tools',
docsUrl: 'https://docs.temporal.io/'
},
'waha': {
name: 'WAHA',
description: 'WhatsApp HTTP API',
@@ -415,9 +436,12 @@
{ cmd: 'make logs s=<service>', desc: 'View logs for specific service' },
{ cmd: 'make monitor', desc: 'Live CPU/memory monitoring' },
{ cmd: 'make restart', desc: 'Restart all services' },
{ cmd: 'make stop', desc: 'Stop all services' },
{ cmd: 'make start', desc: 'Start all services' },
{ cmd: 'make show-restarts', desc: 'Show restart count per container' },
{ cmd: 'make doctor', desc: 'Run system diagnostics' },
{ cmd: 'make update', desc: 'Update system and services' },
{ cmd: 'make git-pull', desc: 'Update for forks (merge from upstream)' },
{ cmd: 'make import', desc: 'Import n8n workflows (use n=10 to limit)' },
{ cmd: 'make clean', desc: 'Remove unused Docker resources' }
];
@@ -841,6 +865,7 @@
const servicesContainer = document.getElementById('services-container');
const quickstartContainer = document.getElementById('quickstart-container');
const commandsContainer = document.getElementById('commands-container');
const changelogContainer = document.getElementById('changelog-container');
const domainInfo = document.getElementById('domain-info');
/**
@@ -956,6 +981,26 @@
commandsContainer.appendChild(grid);
}
/**
* Render changelog content
*/
function renderChangelog(content) {
if (!changelogContainer) return;
changelogContainer.innerHTML = '';
if (!content) {
changelogContainer.innerHTML = `
<p class="text-gray-500 text-center py-8">Changelog not available</p>
`;
return;
}
const pre = document.createElement('pre');
pre.className = 'text-sm text-gray-300 font-mono whitespace-pre-wrap break-words leading-relaxed';
pre.textContent = content;
changelogContainer.appendChild(pre);
}
/**
* Render error state in services container
*/
@@ -981,14 +1026,26 @@
// Always render commands (static content)
renderCommands();
try {
const response = await fetch('data.json');
// Fetch both JSON files in parallel for better performance
// Each fetch is handled independently - changelog failure won't affect main data
const [changelogResult, dataResult] = await Promise.allSettled([
fetch('changelog.json').then(r => r.ok ? r.json() : null),
fetch('data.json').then(r => r.ok ? r.json() : Promise.reject(new Error(`HTTP ${r.status}`)))
]);
if (!response.ok) {
throw new Error(`Failed to load data (${response.status})`);
// Handle changelog (independent - failures don't break the page)
if (changelogResult.status === 'fulfilled' && changelogResult.value?.content) {
renderChangelog(changelogResult.value.content);
} else {
if (changelogResult.status === 'rejected') {
console.error('Error loading changelog:', changelogResult.reason);
}
renderChangelog(null);
}
const data = await response.json();
// Handle main data
if (dataResult.status === 'fulfilled' && dataResult.value) {
const data = dataResult.value;
// Update domain info
if (domainInfo) {
@@ -1006,9 +1063,8 @@
// Render quick start
renderQuickStart(data.quick_start);
} catch (error) {
console.error('Error loading data:', error);
} else {
console.error('Error loading data:', dataResult.reason);
// Show error in UI
renderServicesError();

View File

@@ -51,7 +51,7 @@
}
::-webkit-scrollbar-track {
background: rgba(17, 17, 17, 0.8);
background: transparent;
border-radius: 5px;
}
@@ -198,6 +198,23 @@
<div class="gradient-line my-8" aria-hidden="true"></div>
<!-- Changelog Section -->
<section class="mb-16">
<div class="flex items-center gap-3 mb-6">
<div class="w-10 h-10 rounded-lg bg-brand/10 border border-brand/20 flex items-center justify-center"
data-section-icon="changelog"></div>
<h2 class="text-2xl font-semibold text-white">Changelog</h2>
</div>
<div id="changelog-container"
class="bg-surface-100 rounded-xl border border-surface-400 p-6 overflow-y-auto"
style="max-height: 444px;">
<!-- Changelog content will be injected here by JavaScript -->
<div class="animate-pulse h-32"></div>
</div>
</section>
<div class="gradient-line my-8" aria-hidden="true"></div>
<!-- Documentation Section -->
<section class="mb-16">
<div class="flex items-center gap-3 mb-6">