docs: add implementation plan for externalizing compose templates
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,961 @@
|
||||
# Externalize Docker Compose Templates — Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Replace inline docker-compose generation in installer scripts with static template files, reducing duplication and enabling user customization.
|
||||
|
||||
**Architecture:** Static YAML templates in `installer/templates/` are copied to the install directory. The installer writes `.env` (including `COMPOSE_FILE` to select which templates are active) and runs `docker compose up -d`. Conditional features (TLS, monitoring) are handled via compose file layering and `.env` variables instead of heredoc injection.
|
||||
|
||||
**Tech Stack:** Docker Compose v2, YAML, Bash, PowerShell
|
||||
|
||||
**Spec:** `docs/superpowers/specs/2026-04-15-externalize-compose-templates-design.md`
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Create `docker-compose.yml` (infra base template)
|
||||
|
||||
**Files:**
|
||||
- Create: `installer/templates/docker-compose.yml`
|
||||
|
||||
This is the shared infrastructure base — always loaded regardless of deployment mode.
|
||||
|
||||
- [ ] **Step 1: Create the infra base template**
|
||||
|
||||
```yaml
|
||||
# Cameleer Infrastructure
|
||||
# Shared base — always loaded. Mode-specific services in separate compose files.
|
||||
|
||||
services:
|
||||
cameleer-traefik:
|
||||
image: ${TRAEFIK_IMAGE:-gitea.siegeln.net/cameleer/cameleer-traefik}:${VERSION:-latest}
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${HTTP_PORT:-80}:80"
|
||||
- "${HTTPS_PORT:-443}:443"
|
||||
- "${LOGTO_CONSOLE_BIND:-127.0.0.1}:${LOGTO_CONSOLE_PORT:-3002}:3002"
|
||||
environment:
|
||||
PUBLIC_HOST: ${PUBLIC_HOST:-localhost}
|
||||
CERT_FILE: ${CERT_FILE:-}
|
||||
KEY_FILE: ${KEY_FILE:-}
|
||||
CA_FILE: ${CA_FILE:-}
|
||||
volumes:
|
||||
- cameleer-certs:/certs
|
||||
- ${DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock:ro
|
||||
labels:
|
||||
- "prometheus.io/scrape=true"
|
||||
- "prometheus.io/port=8082"
|
||||
- "prometheus.io/path=/metrics"
|
||||
networks:
|
||||
- cameleer
|
||||
- cameleer-traefik
|
||||
- monitoring
|
||||
|
||||
cameleer-postgres:
|
||||
image: ${POSTGRES_IMAGE:-gitea.siegeln.net/cameleer/cameleer-postgres}:${VERSION:-latest}
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: ${POSTGRES_DB:-cameleer_saas}
|
||||
POSTGRES_USER: ${POSTGRES_USER:-cameleer}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD must be set in .env}
|
||||
volumes:
|
||||
- cameleer-pgdata:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER:-cameleer} -d $${POSTGRES_DB:-cameleer_saas}"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- cameleer
|
||||
- monitoring
|
||||
|
||||
cameleer-clickhouse:
|
||||
image: ${CLICKHOUSE_IMAGE:-gitea.siegeln.net/cameleer/cameleer-clickhouse}:${VERSION:-latest}
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:?CLICKHOUSE_PASSWORD must be set in .env}
|
||||
volumes:
|
||||
- cameleer-chdata:/var/lib/clickhouse
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "clickhouse-client --password $${CLICKHOUSE_PASSWORD} --query 'SELECT 1'"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
labels:
|
||||
- "prometheus.io/scrape=true"
|
||||
- "prometheus.io/port=9363"
|
||||
- "prometheus.io/path=/metrics"
|
||||
networks:
|
||||
- cameleer
|
||||
- monitoring
|
||||
|
||||
volumes:
|
||||
cameleer-pgdata:
|
||||
cameleer-chdata:
|
||||
cameleer-certs:
|
||||
|
||||
networks:
|
||||
cameleer:
|
||||
driver: bridge
|
||||
cameleer-traefik:
|
||||
name: cameleer-traefik
|
||||
driver: bridge
|
||||
monitoring:
|
||||
name: cameleer-monitoring-noop
|
||||
```
|
||||
|
||||
Key changes from the generated version:
|
||||
- Logto console port always present with `LOGTO_CONSOLE_BIND` controlling exposure
|
||||
- Prometheus labels unconditional on traefik and clickhouse
|
||||
- `monitoring` network defined as local noop bridge
|
||||
- All services join `monitoring` network
|
||||
- `POSTGRES_DB` uses `${POSTGRES_DB:-cameleer_saas}` (parameterized — standalone overrides via `.env`)
|
||||
- Password variables use `:?` fail-if-unset
|
||||
|
||||
Note: The SaaS mode uses `cameleer-postgres` (custom multi-DB image) while standalone uses `postgres:16-alpine`. The `POSTGRES_IMAGE` variable already handles this — the infra base uses `${POSTGRES_IMAGE:-...}` and standalone `.env` sets `POSTGRES_IMAGE=postgres:16-alpine`.
|
||||
|
||||
- [ ] **Step 2: Verify YAML is valid**
|
||||
|
||||
Run: `python -c "import yaml; yaml.safe_load(open('installer/templates/docker-compose.yml'))"`
|
||||
Expected: No output (valid YAML). If python/yaml not available, use `docker compose -f installer/templates/docker-compose.yml config --quiet` (will fail on unset vars, but validates structure).
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add installer/templates/docker-compose.yml
|
||||
git commit -m "feat(installer): add infra base docker-compose template"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Create `docker-compose.saas.yml` (SaaS mode template)
|
||||
|
||||
**Files:**
|
||||
- Create: `installer/templates/docker-compose.saas.yml`
|
||||
|
||||
SaaS-specific services: Logto identity provider and cameleer-saas management plane.
|
||||
|
||||
- [ ] **Step 1: Create the SaaS template**
|
||||
|
||||
```yaml
|
||||
# Cameleer SaaS — Logto + management plane
|
||||
# Loaded in SaaS deployment mode
|
||||
|
||||
services:
|
||||
cameleer-logto:
|
||||
image: ${LOGTO_IMAGE:-gitea.siegeln.net/cameleer/cameleer-logto}:${VERSION:-latest}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
cameleer-postgres:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
DB_URL: postgres://${POSTGRES_USER:-cameleer}:${POSTGRES_PASSWORD}@cameleer-postgres:5432/logto
|
||||
ENDPOINT: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}
|
||||
ADMIN_ENDPOINT: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}:${LOGTO_CONSOLE_PORT:-3002}
|
||||
TRUST_PROXY_HEADER: 1
|
||||
NODE_TLS_REJECT_UNAUTHORIZED: "${NODE_TLS_REJECT:-0}"
|
||||
LOGTO_ENDPOINT: http://cameleer-logto:3001
|
||||
LOGTO_ADMIN_ENDPOINT: http://cameleer-logto:3002
|
||||
LOGTO_PUBLIC_ENDPOINT: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}
|
||||
PUBLIC_HOST: ${PUBLIC_HOST:-localhost}
|
||||
PUBLIC_PROTOCOL: ${PUBLIC_PROTOCOL:-https}
|
||||
PG_HOST: cameleer-postgres
|
||||
PG_USER: ${POSTGRES_USER:-cameleer}
|
||||
PG_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
PG_DB_SAAS: cameleer_saas
|
||||
SAAS_ADMIN_USER: ${SAAS_ADMIN_USER:-admin}
|
||||
SAAS_ADMIN_PASS: ${SAAS_ADMIN_PASS:?SAAS_ADMIN_PASS must be set in .env}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "node -e \"require('http').get('http://localhost:3001/oidc/.well-known/openid-configuration', r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))\" && test -f /data/logto-bootstrap.json"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 60
|
||||
start_period: 30s
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.cameleer-logto.rule=PathPrefix(`/`)
|
||||
- traefik.http.routers.cameleer-logto.priority=1
|
||||
- traefik.http.routers.cameleer-logto.entrypoints=websecure
|
||||
- traefik.http.routers.cameleer-logto.tls=true
|
||||
- traefik.http.routers.cameleer-logto.service=cameleer-logto
|
||||
- traefik.http.routers.cameleer-logto.middlewares=cameleer-logto-cors
|
||||
- "traefik.http.middlewares.cameleer-logto-cors.headers.accessControlAllowOriginList=${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}:${LOGTO_CONSOLE_PORT:-3002}"
|
||||
- traefik.http.middlewares.cameleer-logto-cors.headers.accessControlAllowMethods=GET,POST,PUT,PATCH,DELETE,OPTIONS
|
||||
- traefik.http.middlewares.cameleer-logto-cors.headers.accessControlAllowHeaders=Authorization,Content-Type
|
||||
- traefik.http.middlewares.cameleer-logto-cors.headers.accessControlAllowCredentials=true
|
||||
- traefik.http.services.cameleer-logto.loadbalancer.server.port=3001
|
||||
- traefik.http.routers.cameleer-logto-console.rule=PathPrefix(`/`)
|
||||
- traefik.http.routers.cameleer-logto-console.entrypoints=admin-console
|
||||
- traefik.http.routers.cameleer-logto-console.tls=true
|
||||
- traefik.http.routers.cameleer-logto-console.service=cameleer-logto-console
|
||||
- traefik.http.services.cameleer-logto-console.loadbalancer.server.port=3002
|
||||
volumes:
|
||||
- cameleer-bootstrapdata:/data
|
||||
networks:
|
||||
- cameleer
|
||||
- monitoring
|
||||
|
||||
cameleer-saas:
|
||||
image: ${CAMELEER_IMAGE:-gitea.siegeln.net/cameleer/cameleer-saas}:${VERSION:-latest}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
cameleer-logto:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
# SaaS database
|
||||
SPRING_DATASOURCE_URL: jdbc:postgresql://cameleer-postgres:5432/cameleer_saas
|
||||
SPRING_DATASOURCE_USERNAME: ${POSTGRES_USER:-cameleer}
|
||||
SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
# Identity (Logto)
|
||||
CAMELEER_SAAS_IDENTITY_LOGTOENDPOINT: http://cameleer-logto:3001
|
||||
CAMELEER_SAAS_IDENTITY_LOGTOPUBLICENDPOINT: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}
|
||||
# Provisioning — passed to per-tenant server containers
|
||||
CAMELEER_SAAS_PROVISIONING_PUBLICHOST: ${PUBLIC_HOST:-localhost}
|
||||
CAMELEER_SAAS_PROVISIONING_PUBLICPROTOCOL: ${PUBLIC_PROTOCOL:-https}
|
||||
CAMELEER_SAAS_PROVISIONING_NETWORKNAME: ${COMPOSE_PROJECT_NAME:-cameleer-saas}_cameleer
|
||||
CAMELEER_SAAS_PROVISIONING_TRAEFIKNETWORK: cameleer-traefik
|
||||
CAMELEER_SAAS_PROVISIONING_DATASOURCEUSERNAME: ${POSTGRES_USER:-cameleer}
|
||||
CAMELEER_SAAS_PROVISIONING_DATASOURCEPASSWORD: ${POSTGRES_PASSWORD}
|
||||
CAMELEER_SAAS_PROVISIONING_CLICKHOUSEPASSWORD: ${CLICKHOUSE_PASSWORD}
|
||||
CAMELEER_SAAS_PROVISIONING_SERVERIMAGE: ${CAMELEER_SAAS_PROVISIONING_SERVERIMAGE:-gitea.siegeln.net/cameleer/cameleer-server:latest}
|
||||
CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE: ${CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE:-gitea.siegeln.net/cameleer/cameleer-server-ui:latest}
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.saas.rule=PathPrefix(`/platform`)
|
||||
- traefik.http.routers.saas.entrypoints=websecure
|
||||
- traefik.http.routers.saas.tls=true
|
||||
- traefik.http.services.saas.loadbalancer.server.port=8080
|
||||
- "prometheus.io/scrape=true"
|
||||
- "prometheus.io/port=8080"
|
||||
- "prometheus.io/path=/platform/actuator/prometheus"
|
||||
volumes:
|
||||
- cameleer-bootstrapdata:/data/bootstrap:ro
|
||||
- cameleer-certs:/certs
|
||||
- ${DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock
|
||||
group_add:
|
||||
- "${DOCKER_GID:-0}"
|
||||
networks:
|
||||
- cameleer
|
||||
- monitoring
|
||||
|
||||
volumes:
|
||||
cameleer-bootstrapdata:
|
||||
|
||||
networks:
|
||||
monitoring:
|
||||
name: cameleer-monitoring-noop
|
||||
```
|
||||
|
||||
Key changes:
|
||||
- Logto console traefik labels always included (harmless when port is localhost-only)
|
||||
- Prometheus labels on cameleer-saas always included
|
||||
- `DOCKER_GID` read from `.env` via `${DOCKER_GID:-0}` instead of inline `stat`
|
||||
- Both services join `monitoring` network
|
||||
- `monitoring` network redefined as noop bridge (compose merges with base definition)
|
||||
|
||||
- [ ] **Step 2: Commit**
|
||||
|
||||
```bash
|
||||
git add installer/templates/docker-compose.saas.yml
|
||||
git commit -m "feat(installer): add SaaS docker-compose template"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Create `docker-compose.server.yml` (standalone mode template)
|
||||
|
||||
**Files:**
|
||||
- Create: `installer/templates/docker-compose.server.yml`
|
||||
- Create: `installer/templates/traefik-dynamic.yml`
|
||||
|
||||
Standalone-specific services: cameleer-server + server-ui. Also includes the traefik dynamic config that standalone mode needs (overrides the baked-in SaaS redirect).
|
||||
|
||||
- [ ] **Step 1: Create the standalone template**
|
||||
|
||||
```yaml
|
||||
# Cameleer Server (standalone)
|
||||
# Loaded in standalone deployment mode
|
||||
|
||||
services:
|
||||
cameleer-traefik:
|
||||
volumes:
|
||||
- ./traefik-dynamic.yml:/etc/traefik/dynamic.yml:ro
|
||||
|
||||
cameleer-postgres:
|
||||
image: postgres:16-alpine
|
||||
environment:
|
||||
POSTGRES_DB: ${POSTGRES_DB:-cameleer}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER:-cameleer} -d $${POSTGRES_DB:-cameleer}"]
|
||||
|
||||
cameleer-server:
|
||||
image: ${SERVER_IMAGE:-gitea.siegeln.net/cameleer/cameleer-server}:${VERSION:-latest}
|
||||
container_name: cameleer-server
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
cameleer-postgres:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
CAMELEER_SERVER_TENANT_ID: default
|
||||
SPRING_DATASOURCE_URL: jdbc:postgresql://cameleer-postgres:5432/${POSTGRES_DB:-cameleer}?currentSchema=tenant_default
|
||||
SPRING_DATASOURCE_USERNAME: ${POSTGRES_USER:-cameleer}
|
||||
SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
CAMELEER_SERVER_CLICKHOUSE_URL: jdbc:clickhouse://cameleer-clickhouse:8123/cameleer
|
||||
CAMELEER_SERVER_CLICKHOUSE_USERNAME: default
|
||||
CAMELEER_SERVER_CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD}
|
||||
CAMELEER_SERVER_SECURITY_BOOTSTRAPTOKEN: ${BOOTSTRAP_TOKEN:?BOOTSTRAP_TOKEN must be set in .env}
|
||||
CAMELEER_SERVER_SECURITY_UIUSER: ${SERVER_ADMIN_USER:-admin}
|
||||
CAMELEER_SERVER_SECURITY_UIPASSWORD: ${SERVER_ADMIN_PASS:?SERVER_ADMIN_PASS must be set in .env}
|
||||
CAMELEER_SERVER_SECURITY_CORSALLOWEDORIGINS: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}
|
||||
CAMELEER_SERVER_RUNTIME_ENABLED: "true"
|
||||
CAMELEER_SERVER_RUNTIME_SERVERURL: http://cameleer-server:8081
|
||||
CAMELEER_SERVER_RUNTIME_ROUTINGDOMAIN: ${PUBLIC_HOST:-localhost}
|
||||
CAMELEER_SERVER_RUNTIME_ROUTINGMODE: path
|
||||
CAMELEER_SERVER_RUNTIME_JARSTORAGEPATH: /data/jars
|
||||
CAMELEER_SERVER_RUNTIME_DOCKERNETWORK: cameleer-apps
|
||||
CAMELEER_SERVER_RUNTIME_JARDOCKERVOLUME: cameleer-jars
|
||||
CAMELEER_SERVER_RUNTIME_BASEIMAGE: gitea.siegeln.net/cameleer/cameleer-runtime-base:${VERSION:-latest}
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.server-api.rule=PathPrefix(`/api`)
|
||||
- traefik.http.routers.server-api.entrypoints=websecure
|
||||
- traefik.http.routers.server-api.tls=true
|
||||
- traefik.http.services.server-api.loadbalancer.server.port=8081
|
||||
- traefik.docker.network=cameleer-traefik
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -sf http://localhost:8081/api/v1/health || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
start_period: 30s
|
||||
volumes:
|
||||
- jars:/data/jars
|
||||
- cameleer-certs:/certs:ro
|
||||
- ${DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock
|
||||
group_add:
|
||||
- "${DOCKER_GID:-0}"
|
||||
networks:
|
||||
- cameleer
|
||||
- cameleer-traefik
|
||||
- cameleer-apps
|
||||
- monitoring
|
||||
|
||||
cameleer-server-ui:
|
||||
image: ${SERVER_UI_IMAGE:-gitea.siegeln.net/cameleer/cameleer-server-ui}:${VERSION:-latest}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
cameleer-server:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
CAMELEER_API_URL: http://cameleer-server:8081
|
||||
BASE_PATH: ""
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.ui.rule=PathPrefix(`/`)
|
||||
- traefik.http.routers.ui.priority=1
|
||||
- traefik.http.routers.ui.entrypoints=websecure
|
||||
- traefik.http.routers.ui.tls=true
|
||||
- traefik.http.services.ui.loadbalancer.server.port=80
|
||||
- traefik.docker.network=cameleer-traefik
|
||||
networks:
|
||||
- cameleer-traefik
|
||||
- monitoring
|
||||
|
||||
volumes:
|
||||
jars:
|
||||
|
||||
networks:
|
||||
cameleer-apps:
|
||||
name: cameleer-apps
|
||||
driver: bridge
|
||||
monitoring:
|
||||
name: cameleer-monitoring-noop
|
||||
```
|
||||
|
||||
Key design decisions:
|
||||
- `cameleer-traefik` and `cameleer-postgres` entries are **overrides** — compose merges them with the base. The postgres image switches to `postgres:16-alpine` and the healthcheck uses `${POSTGRES_DB:-cameleer}` instead of hardcoded `cameleer_saas`. Traefik gets the `traefik-dynamic.yml` volume mount.
|
||||
- `DOCKER_GID` from `.env` via `${DOCKER_GID:-0}`
|
||||
- `BOOTSTRAP_TOKEN` uses `:?` fail-if-unset
|
||||
- Both server and server-ui join `monitoring` network
|
||||
|
||||
- [ ] **Step 2: Create the traefik dynamic config template**
|
||||
|
||||
```yaml
|
||||
tls:
|
||||
stores:
|
||||
default:
|
||||
defaultCertificate:
|
||||
certFile: /certs/cert.pem
|
||||
keyFile: /certs/key.pem
|
||||
```
|
||||
|
||||
This file is only relevant in standalone mode (overrides the baked-in SaaS `/` -> `/platform/` redirect in the traefik image).
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add installer/templates/docker-compose.server.yml installer/templates/traefik-dynamic.yml
|
||||
git commit -m "feat(installer): add standalone docker-compose and traefik templates"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Create overlay templates (TLS + monitoring)
|
||||
|
||||
**Files:**
|
||||
- Create: `installer/templates/docker-compose.tls.yml`
|
||||
- Create: `installer/templates/docker-compose.monitoring.yml`
|
||||
|
||||
- [ ] **Step 1: Create the TLS overlay**
|
||||
|
||||
```yaml
|
||||
# Custom TLS certificates overlay
|
||||
# Adds user-supplied certificate volume to traefik
|
||||
|
||||
services:
|
||||
cameleer-traefik:
|
||||
volumes:
|
||||
- ./certs:/user-certs:ro
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Create the monitoring overlay**
|
||||
|
||||
```yaml
|
||||
# External monitoring network overlay
|
||||
# Overrides the noop monitoring bridge with a real external network
|
||||
|
||||
networks:
|
||||
monitoring:
|
||||
external: true
|
||||
name: ${MONITORING_NETWORK:?MONITORING_NETWORK must be set in .env}
|
||||
```
|
||||
|
||||
This is the key to the monitoring pattern: the base compose files define `monitoring` as a local noop bridge and all services join it. When this overlay is included in `COMPOSE_FILE`, compose merges the network definition — overriding it to point at the real external monitoring network. No per-service entries needed.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add installer/templates/docker-compose.tls.yml installer/templates/docker-compose.monitoring.yml
|
||||
git commit -m "feat(installer): add TLS and monitoring overlay templates"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Create `.env.example`
|
||||
|
||||
**Files:**
|
||||
- Create: `installer/templates/.env.example`
|
||||
|
||||
- [ ] **Step 1: Create the documented variable reference**
|
||||
|
||||
```bash
|
||||
# Cameleer Configuration
|
||||
# Copy this file to .env and fill in the values.
|
||||
# The installer generates .env automatically — this file is for reference.
|
||||
|
||||
# ============================================================
|
||||
# Compose file assembly (set by installer)
|
||||
# ============================================================
|
||||
# SaaS: docker-compose.yml:docker-compose.saas.yml
|
||||
# Standalone: docker-compose.yml:docker-compose.server.yml
|
||||
# Add :docker-compose.tls.yml for custom TLS certificates
|
||||
# Add :docker-compose.monitoring.yml for external monitoring network
|
||||
COMPOSE_FILE=docker-compose.yml:docker-compose.saas.yml
|
||||
|
||||
# ============================================================
|
||||
# Image version
|
||||
# ============================================================
|
||||
VERSION=latest
|
||||
|
||||
# ============================================================
|
||||
# Public access
|
||||
# ============================================================
|
||||
PUBLIC_HOST=localhost
|
||||
PUBLIC_PROTOCOL=https
|
||||
|
||||
# ============================================================
|
||||
# Ports
|
||||
# ============================================================
|
||||
HTTP_PORT=80
|
||||
HTTPS_PORT=443
|
||||
# Set to 0.0.0.0 to expose Logto admin console externally (default: localhost only)
|
||||
# LOGTO_CONSOLE_BIND=0.0.0.0
|
||||
LOGTO_CONSOLE_PORT=3002
|
||||
|
||||
# ============================================================
|
||||
# PostgreSQL
|
||||
# ============================================================
|
||||
POSTGRES_USER=cameleer
|
||||
POSTGRES_PASSWORD=CHANGE_ME
|
||||
# SaaS: cameleer_saas, Standalone: cameleer
|
||||
POSTGRES_DB=cameleer_saas
|
||||
|
||||
# ============================================================
|
||||
# ClickHouse
|
||||
# ============================================================
|
||||
CLICKHOUSE_PASSWORD=CHANGE_ME
|
||||
|
||||
# ============================================================
|
||||
# Admin credentials (SaaS mode)
|
||||
# ============================================================
|
||||
SAAS_ADMIN_USER=admin
|
||||
SAAS_ADMIN_PASS=CHANGE_ME
|
||||
|
||||
# ============================================================
|
||||
# Admin credentials (standalone mode)
|
||||
# ============================================================
|
||||
# SERVER_ADMIN_USER=admin
|
||||
# SERVER_ADMIN_PASS=CHANGE_ME
|
||||
# BOOTSTRAP_TOKEN=CHANGE_ME
|
||||
|
||||
# ============================================================
|
||||
# TLS
|
||||
# ============================================================
|
||||
# Set to 1 to reject unauthorized TLS certificates (production)
|
||||
NODE_TLS_REJECT=0
|
||||
# Custom TLS certificate paths (inside container, set by installer)
|
||||
# CERT_FILE=/user-certs/cert.pem
|
||||
# KEY_FILE=/user-certs/key.pem
|
||||
# CA_FILE=/user-certs/ca.pem
|
||||
|
||||
# ============================================================
|
||||
# Docker
|
||||
# ============================================================
|
||||
DOCKER_SOCKET=/var/run/docker.sock
|
||||
# GID of the docker socket — detected by installer, used for container group_add
|
||||
DOCKER_GID=0
|
||||
|
||||
# ============================================================
|
||||
# Provisioning images (SaaS mode only)
|
||||
# ============================================================
|
||||
# CAMELEER_SAAS_PROVISIONING_SERVERIMAGE=gitea.siegeln.net/cameleer/cameleer-server:latest
|
||||
# CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE=gitea.siegeln.net/cameleer/cameleer-server-ui:latest
|
||||
|
||||
# ============================================================
|
||||
# Monitoring (optional)
|
||||
# ============================================================
|
||||
# External Docker network name for Prometheus scraping.
|
||||
# Only needed when docker-compose.monitoring.yml is in COMPOSE_FILE.
|
||||
# MONITORING_NETWORK=prometheus
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Commit**
|
||||
|
||||
```bash
|
||||
git add installer/templates/.env.example
|
||||
git commit -m "feat(installer): add .env.example with documented variables"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 6: Update `install.sh` — replace compose generation with template copying
|
||||
|
||||
**Files:**
|
||||
- Modify: `installer/install.sh:574-672` (generate_env_file — add COMPOSE_FILE and LOGTO_CONSOLE_BIND)
|
||||
- Modify: `installer/install.sh:674-1135` (replace generate_compose_file + generate_compose_file_standalone with copy_templates)
|
||||
- Modify: `installer/install.sh:1728-1731` (reinstall cleanup — delete template files)
|
||||
- Modify: `installer/install.sh:1696-1710` (upgrade path — copy templates instead of generate)
|
||||
- Modify: `installer/install.sh:1790-1791` (main — call copy_templates instead of generate_compose_file)
|
||||
|
||||
- [ ] **Step 1: Replace `generate_compose_file` and `generate_compose_file_standalone` with `copy_templates`**
|
||||
|
||||
Delete both functions (`generate_compose_file` at line 674 and `generate_compose_file_standalone` at line 934) and replace with:
|
||||
|
||||
```bash
|
||||
copy_templates() {
|
||||
local src
|
||||
src="$(cd "$(dirname "$0")" && pwd)/templates"
|
||||
|
||||
# Base infra — always copied
|
||||
cp "$src/docker-compose.yml" "$INSTALL_DIR/docker-compose.yml"
|
||||
cp "$src/.env.example" "$INSTALL_DIR/.env.example"
|
||||
|
||||
# Mode-specific
|
||||
if [ "$DEPLOYMENT_MODE" = "standalone" ]; then
|
||||
cp "$src/docker-compose.server.yml" "$INSTALL_DIR/docker-compose.server.yml"
|
||||
cp "$src/traefik-dynamic.yml" "$INSTALL_DIR/traefik-dynamic.yml"
|
||||
else
|
||||
cp "$src/docker-compose.saas.yml" "$INSTALL_DIR/docker-compose.saas.yml"
|
||||
fi
|
||||
|
||||
# Optional overlays
|
||||
if [ "$TLS_MODE" = "custom" ]; then
|
||||
cp "$src/docker-compose.tls.yml" "$INSTALL_DIR/docker-compose.tls.yml"
|
||||
fi
|
||||
if [ -n "$MONITORING_NETWORK" ]; then
|
||||
cp "$src/docker-compose.monitoring.yml" "$INSTALL_DIR/docker-compose.monitoring.yml"
|
||||
fi
|
||||
|
||||
log_info "Copied docker-compose templates to $INSTALL_DIR"
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Update `generate_env_file` to include `COMPOSE_FILE`, `LOGTO_CONSOLE_BIND`, and `DOCKER_GID`**
|
||||
|
||||
In the standalone `.env` block (line 577-614), add after the `DOCKER_GID` line:
|
||||
|
||||
```bash
|
||||
# Compose file assembly
|
||||
COMPOSE_FILE=docker-compose.yml:docker-compose.server.yml$([ "$TLS_MODE" = "custom" ] && echo ":docker-compose.tls.yml")$([ -n "$MONITORING_NETWORK" ] && echo ":docker-compose.monitoring.yml")
|
||||
EOF
|
||||
```
|
||||
|
||||
In the SaaS `.env` block (line 617-668), add `LOGTO_CONSOLE_BIND` and `COMPOSE_FILE`. After the `LOGTO_CONSOLE_PORT` line:
|
||||
|
||||
```bash
|
||||
LOGTO_CONSOLE_BIND=$([ "$LOGTO_CONSOLE_EXPOSED" = "true" ] && echo "0.0.0.0" || echo "127.0.0.1")
|
||||
```
|
||||
|
||||
And at the end of the SaaS block, add the `COMPOSE_FILE` line:
|
||||
|
||||
```bash
|
||||
# Compose file assembly
|
||||
COMPOSE_FILE=docker-compose.yml:docker-compose.saas.yml$([ "$TLS_MODE" = "custom" ] && echo ":docker-compose.tls.yml")$([ -n "$MONITORING_NETWORK" ] && echo ":docker-compose.monitoring.yml")
|
||||
```
|
||||
|
||||
Also add the `MONITORING_NETWORK` variable to `.env` when set:
|
||||
|
||||
```bash
|
||||
if [ -n "$MONITORING_NETWORK" ]; then
|
||||
echo "" >> "$f"
|
||||
echo "# Monitoring" >> "$f"
|
||||
echo "MONITORING_NETWORK=${MONITORING_NETWORK}" >> "$f"
|
||||
fi
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Update `main()` — replace `generate_compose_file` call with `copy_templates`**
|
||||
|
||||
At line 1791, change:
|
||||
```bash
|
||||
generate_compose_file
|
||||
```
|
||||
to:
|
||||
```bash
|
||||
copy_templates
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Update `handle_rerun` upgrade path**
|
||||
|
||||
At line 1703, change:
|
||||
```bash
|
||||
generate_compose_file
|
||||
```
|
||||
to:
|
||||
```bash
|
||||
copy_templates
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Update reinstall cleanup to remove template files**
|
||||
|
||||
At lines 1728-1731, update the `rm -f` list to include all possible template files:
|
||||
```bash
|
||||
rm -f "$INSTALL_DIR/.env" "$INSTALL_DIR/.env.bak" "$INSTALL_DIR/.env.example" \
|
||||
"$INSTALL_DIR/docker-compose.yml" "$INSTALL_DIR/docker-compose.saas.yml" \
|
||||
"$INSTALL_DIR/docker-compose.server.yml" "$INSTALL_DIR/docker-compose.tls.yml" \
|
||||
"$INSTALL_DIR/docker-compose.monitoring.yml" "$INSTALL_DIR/traefik-dynamic.yml" \
|
||||
"$INSTALL_DIR/cameleer.conf" "$INSTALL_DIR/credentials.txt" \
|
||||
"$INSTALL_DIR/INSTALL.md"
|
||||
```
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add installer/install.sh
|
||||
git commit -m "refactor(installer): replace sh compose generation with template copying"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 7: Update `install.ps1` — replace compose generation with template copying
|
||||
|
||||
**Files:**
|
||||
- Modify: `installer/install.ps1:574-666` (Generate-EnvFile — add COMPOSE_FILE and LOGTO_CONSOLE_BIND)
|
||||
- Modify: `installer/install.ps1:671-1105` (replace Generate-ComposeFile + Generate-ComposeFileStandalone with Copy-Templates)
|
||||
- Modify: `installer/install.ps1:1706-1723` (upgrade path)
|
||||
- Modify: `installer/install.ps1:1746` (reinstall cleanup)
|
||||
- Modify: `installer/install.ps1:1797-1798` (Main — call Copy-Templates)
|
||||
|
||||
- [ ] **Step 1: Replace `Generate-ComposeFile` and `Generate-ComposeFileStandalone` with `Copy-Templates`**
|
||||
|
||||
Delete both functions and replace with:
|
||||
|
||||
```powershell
|
||||
function Copy-Templates {
|
||||
$c = $script:cfg
|
||||
$src = Join-Path $PSScriptRoot 'templates'
|
||||
|
||||
# Base infra — always copied
|
||||
Copy-Item (Join-Path $src 'docker-compose.yml') (Join-Path $c.InstallDir 'docker-compose.yml') -Force
|
||||
Copy-Item (Join-Path $src '.env.example') (Join-Path $c.InstallDir '.env.example') -Force
|
||||
|
||||
# Mode-specific
|
||||
if ($c.DeploymentMode -eq 'standalone') {
|
||||
Copy-Item (Join-Path $src 'docker-compose.server.yml') (Join-Path $c.InstallDir 'docker-compose.server.yml') -Force
|
||||
Copy-Item (Join-Path $src 'traefik-dynamic.yml') (Join-Path $c.InstallDir 'traefik-dynamic.yml') -Force
|
||||
} else {
|
||||
Copy-Item (Join-Path $src 'docker-compose.saas.yml') (Join-Path $c.InstallDir 'docker-compose.saas.yml') -Force
|
||||
}
|
||||
|
||||
# Optional overlays
|
||||
if ($c.TlsMode -eq 'custom') {
|
||||
Copy-Item (Join-Path $src 'docker-compose.tls.yml') (Join-Path $c.InstallDir 'docker-compose.tls.yml') -Force
|
||||
}
|
||||
if ($c.MonitoringNetwork) {
|
||||
Copy-Item (Join-Path $src 'docker-compose.monitoring.yml') (Join-Path $c.InstallDir 'docker-compose.monitoring.yml') -Force
|
||||
}
|
||||
|
||||
Log-Info "Copied docker-compose templates to $($c.InstallDir)"
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Update `Generate-EnvFile` to include `COMPOSE_FILE`, `LOGTO_CONSOLE_BIND`, and `MONITORING_NETWORK`**
|
||||
|
||||
In the standalone `.env` content block, add after `DOCKER_GID`:
|
||||
|
||||
```powershell
|
||||
$composeFile = 'docker-compose.yml:docker-compose.server.yml'
|
||||
if ($c.TlsMode -eq 'custom') { $composeFile += ':docker-compose.tls.yml' }
|
||||
if ($c.MonitoringNetwork) { $composeFile += ':docker-compose.monitoring.yml' }
|
||||
```
|
||||
|
||||
Then append to `$content`:
|
||||
```powershell
|
||||
$content += "`n`n# Compose file assembly`nCOMPOSE_FILE=$composeFile"
|
||||
if ($c.MonitoringNetwork) {
|
||||
$content += "`n`n# Monitoring`nMONITORING_NETWORK=$($c.MonitoringNetwork)"
|
||||
}
|
||||
```
|
||||
|
||||
In the SaaS `.env` content block, add `LOGTO_CONSOLE_BIND` after `LOGTO_CONSOLE_PORT`:
|
||||
|
||||
```powershell
|
||||
$consoleBind = if ($c.LogtoConsoleExposed -eq 'true') { '0.0.0.0' } else { '127.0.0.1' }
|
||||
```
|
||||
|
||||
Add to the content string: `LOGTO_CONSOLE_BIND=$consoleBind`
|
||||
|
||||
Build `COMPOSE_FILE`:
|
||||
```powershell
|
||||
$composeFile = 'docker-compose.yml:docker-compose.saas.yml'
|
||||
if ($c.TlsMode -eq 'custom') { $composeFile += ':docker-compose.tls.yml' }
|
||||
if ($c.MonitoringNetwork) { $composeFile += ':docker-compose.monitoring.yml' }
|
||||
```
|
||||
|
||||
And append to `$content`:
|
||||
```powershell
|
||||
$content += "`n`n# Compose file assembly`nCOMPOSE_FILE=$composeFile"
|
||||
if ($c.MonitoringNetwork) {
|
||||
$content += "`n`n# Monitoring`nMONITORING_NETWORK=$($c.MonitoringNetwork)"
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Update `Main` — replace `Generate-ComposeFile` call with `Copy-Templates`**
|
||||
|
||||
At line 1798, change:
|
||||
```powershell
|
||||
Generate-ComposeFile
|
||||
```
|
||||
to:
|
||||
```powershell
|
||||
Copy-Templates
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Update `Handle-Rerun` upgrade path**
|
||||
|
||||
At line 1716, change:
|
||||
```powershell
|
||||
Generate-ComposeFile
|
||||
```
|
||||
to:
|
||||
```powershell
|
||||
Copy-Templates
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Update reinstall cleanup to remove template files**
|
||||
|
||||
At line 1746, update the filename list:
|
||||
```powershell
|
||||
foreach ($fname in @('.env','.env.bak','.env.example','docker-compose.yml','docker-compose.saas.yml','docker-compose.server.yml','docker-compose.tls.yml','docker-compose.monitoring.yml','traefik-dynamic.yml','cameleer.conf','credentials.txt','INSTALL.md')) {
|
||||
```
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add installer/install.ps1
|
||||
git commit -m "refactor(installer): replace ps1 compose generation with template copying"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 8: Update existing generated install and clean up
|
||||
|
||||
**Files:**
|
||||
- Modify: `installer/cameleer/docker-compose.yml` (replace with template copy for dev environment)
|
||||
|
||||
- [ ] **Step 1: Remove the old generated docker-compose.yml from the cameleer/ directory**
|
||||
|
||||
The `installer/cameleer/` directory contains a previously generated install. The `docker-compose.yml` there is now stale — it was generated by the old inline method. Since this is a dev environment output, remove it (it will be recreated by running the installer with the new template approach).
|
||||
|
||||
```bash
|
||||
git rm installer/cameleer/docker-compose.yml
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Add `installer/cameleer/` to `.gitignore` if not already there**
|
||||
|
||||
The install output directory should not be tracked. Check if `.gitignore` already covers it. If not, add:
|
||||
|
||||
```
|
||||
installer/cameleer/
|
||||
```
|
||||
|
||||
This prevents generated `.env`, `credentials.txt`, and compose files from being committed.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add -A installer/cameleer/ .gitignore
|
||||
git commit -m "chore(installer): remove generated install output, add to gitignore"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 9: Verify the templates produce equivalent output
|
||||
|
||||
**Files:** (no changes — verification only)
|
||||
|
||||
- [ ] **Step 1: Compare template output against the old generated compose**
|
||||
|
||||
Create a temporary `.env` file and run `docker compose config` to render the resolved compose. Compare against the old generated output:
|
||||
|
||||
```bash
|
||||
cd installer/cameleer
|
||||
# Back up old generated file for comparison
|
||||
cp docker-compose.yml docker-compose.old.yml 2>/dev/null || true
|
||||
|
||||
# Create a test .env that exercises the SaaS path
|
||||
cat > /tmp/test-saas.env << 'EOF'
|
||||
COMPOSE_FILE=docker-compose.yml:docker-compose.saas.yml
|
||||
VERSION=latest
|
||||
PUBLIC_HOST=test.example.com
|
||||
PUBLIC_PROTOCOL=https
|
||||
HTTP_PORT=80
|
||||
HTTPS_PORT=443
|
||||
LOGTO_CONSOLE_PORT=3002
|
||||
LOGTO_CONSOLE_BIND=0.0.0.0
|
||||
POSTGRES_USER=cameleer
|
||||
POSTGRES_PASSWORD=testpass
|
||||
POSTGRES_DB=cameleer_saas
|
||||
CLICKHOUSE_PASSWORD=testpass
|
||||
SAAS_ADMIN_USER=admin
|
||||
SAAS_ADMIN_PASS=testpass
|
||||
NODE_TLS_REJECT=0
|
||||
DOCKER_SOCKET=/var/run/docker.sock
|
||||
DOCKER_GID=0
|
||||
CAMELEER_SAAS_PROVISIONING_SERVERIMAGE=gitea.siegeln.net/cameleer/cameleer-server:latest
|
||||
CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE=gitea.siegeln.net/cameleer/cameleer-server-ui:latest
|
||||
EOF
|
||||
|
||||
# Render the new templates
|
||||
cd ../templates
|
||||
docker compose --env-file /tmp/test-saas.env config
|
||||
```
|
||||
|
||||
Expected: A fully resolved compose with all 5 services (traefik, postgres, clickhouse, logto, saas), correct environment variables, and the monitoring noop network.
|
||||
|
||||
- [ ] **Step 2: Test standalone mode rendering**
|
||||
|
||||
```bash
|
||||
cat > /tmp/test-standalone.env << 'EOF'
|
||||
COMPOSE_FILE=docker-compose.yml:docker-compose.server.yml
|
||||
VERSION=latest
|
||||
PUBLIC_HOST=test.example.com
|
||||
PUBLIC_PROTOCOL=https
|
||||
HTTP_PORT=80
|
||||
HTTPS_PORT=443
|
||||
POSTGRES_IMAGE=postgres:16-alpine
|
||||
POSTGRES_USER=cameleer
|
||||
POSTGRES_PASSWORD=testpass
|
||||
POSTGRES_DB=cameleer
|
||||
CLICKHOUSE_PASSWORD=testpass
|
||||
SERVER_ADMIN_USER=admin
|
||||
SERVER_ADMIN_PASS=testpass
|
||||
BOOTSTRAP_TOKEN=testtoken
|
||||
DOCKER_SOCKET=/var/run/docker.sock
|
||||
DOCKER_GID=0
|
||||
EOF
|
||||
|
||||
cd ../templates
|
||||
docker compose --env-file /tmp/test-standalone.env config
|
||||
```
|
||||
|
||||
Expected: 5 services (traefik, postgres with `postgres:16-alpine` image, clickhouse, server, server-ui). Postgres `POSTGRES_DB` should be `cameleer`. Server should have all env vars resolved.
|
||||
|
||||
- [ ] **Step 3: Test with TLS + monitoring overlays**
|
||||
|
||||
```bash
|
||||
cat > /tmp/test-full.env << 'EOF'
|
||||
COMPOSE_FILE=docker-compose.yml:docker-compose.saas.yml:docker-compose.tls.yml:docker-compose.monitoring.yml
|
||||
VERSION=latest
|
||||
PUBLIC_HOST=test.example.com
|
||||
PUBLIC_PROTOCOL=https
|
||||
HTTP_PORT=80
|
||||
HTTPS_PORT=443
|
||||
LOGTO_CONSOLE_PORT=3002
|
||||
LOGTO_CONSOLE_BIND=0.0.0.0
|
||||
POSTGRES_USER=cameleer
|
||||
POSTGRES_PASSWORD=testpass
|
||||
POSTGRES_DB=cameleer_saas
|
||||
CLICKHOUSE_PASSWORD=testpass
|
||||
SAAS_ADMIN_USER=admin
|
||||
SAAS_ADMIN_PASS=testpass
|
||||
NODE_TLS_REJECT=0
|
||||
DOCKER_SOCKET=/var/run/docker.sock
|
||||
DOCKER_GID=0
|
||||
MONITORING_NETWORK=prometheus
|
||||
CAMELEER_SAAS_PROVISIONING_SERVERIMAGE=gitea.siegeln.net/cameleer/cameleer-server:latest
|
||||
CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE=gitea.siegeln.net/cameleer/cameleer-server-ui:latest
|
||||
EOF
|
||||
|
||||
cd ../templates
|
||||
docker compose --env-file /tmp/test-full.env config
|
||||
```
|
||||
|
||||
Expected: Same as SaaS mode but with `./certs:/user-certs:ro` volume on traefik and the `monitoring` network declared as `external: true` with name `prometheus`.
|
||||
|
||||
- [ ] **Step 4: Clean up temp files**
|
||||
|
||||
```bash
|
||||
rm -f /tmp/test-saas.env /tmp/test-standalone.env /tmp/test-full.env
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Commit verification results as a note (optional)**
|
||||
|
||||
No code changes — this task is verification only. If all checks pass, proceed to the final commit.
|
||||
|
||||
---
|
||||
|
||||
### Task 10: Final commit — update CLAUDE.md deployment modes table
|
||||
|
||||
**Files:**
|
||||
- Modify: `CLAUDE.md` (update Deployment Modes section to reference template files)
|
||||
|
||||
- [ ] **Step 1: Update the deployment modes documentation**
|
||||
|
||||
In the "Deployment Modes (installer)" section of CLAUDE.md, add a note about the template-based approach:
|
||||
|
||||
After the deployment modes table, add:
|
||||
|
||||
```markdown
|
||||
The installer uses static docker-compose templates in `installer/templates/`. Templates are copied to the install directory and composed via `COMPOSE_FILE` in `.env`:
|
||||
- `docker-compose.yml` — shared infrastructure (traefik, postgres, clickhouse)
|
||||
- `docker-compose.saas.yml` — SaaS mode (logto, cameleer-saas)
|
||||
- `docker-compose.server.yml` — standalone mode (server, server-ui)
|
||||
- `docker-compose.tls.yml` — overlay: custom TLS cert volume
|
||||
- `docker-compose.monitoring.yml` — overlay: external monitoring network
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Commit**
|
||||
|
||||
```bash
|
||||
git add CLAUDE.md
|
||||
git commit -m "docs: update CLAUDE.md with template-based installer architecture"
|
||||
```
|
||||
Reference in New Issue
Block a user