Compare commits

...

17 Commits

Author SHA1 Message Date
hsiegeln
531a17397b fix: validate admin email format in SaaS mode
Require user@domain.tld format (must contain @ and dot in domain).
Interactive mode loops until valid; silent mode exits with error.
Default changed from 'admin' to 'admin@<PUBLIC_HOST>' in SaaS mode.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 21:03:45 +02:00
hsiegeln
21ea9515a2 feat: unify admin identity — SAAS_ADMIN_USER is the email in SaaS mode
Move deployment mode question before admin credentials so the installer
can validate email format in SaaS mode. Remove separate SAAS_ADMIN_EMAIL
— the admin user value IS the email address. In standalone mode, any
username is still accepted.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 20:45:25 +02:00
hsiegeln
0da26160c6 feat: add SAAS_ADMIN_EMAIL to both installers
Derive admin email from <ADMIN_USER>@<PUBLIC_HOST> by default.
Supports override via --admin-email CLI flag, SAAS_ADMIN_EMAIL env var,
or admin_email in cameleer.conf. Written to .env for bootstrap.
2026-04-25 20:26:38 +02:00
hsiegeln
b2259328d3 feat: enforce email as primary user identity in SaaS mode
Add SAAS_ADMIN_EMAIL env var (defaults to <user>@<host>). Pass to
bootstrap for admin user creation with primaryEmail. Update README
config reference and .env.example to document the email identity
requirement.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 20:23:15 +02:00
8227483580 install.ps1 aktualisiert 2026-04-25 19:50:24 +02:00
hsiegeln
1ef0016965 docs: update README and .env.example for SMTP removal
SMTP configuration is now managed at runtime via the vendor admin UI.
Remove SMTP config reference table, CLI flags from silent install example,
and env vars from .env.example.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 18:16:12 +02:00
hsiegeln
ec1c1f92d7 docs: update installer CLAUDE.md to reflect SMTP removal 2026-04-25 18:09:50 +02:00
hsiegeln
4037fb9dfb feat: remove SMTP configuration from PowerShell installer 2026-04-25 18:08:48 +02:00
hsiegeln
35240e0374 feat: remove SMTP configuration from bash installer 2026-04-25 18:08:44 +02:00
hsiegeln
4885c240e3 feat: remove SMTP env vars from Logto compose template
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 18:05:27 +02:00
hsiegeln
4380aa790d fix: single-quote passwords in .env to handle special characters
Passwords with $, &, ;, [, etc. were written unquoted to .env and
cameleer.conf, causing Docker Compose to mangle them. Now all password
and secret fields are written as KEY='value' with embedded single
quotes escaped as '\''.

Also removes inline DB_URL from docker-compose.saas.yml — the Logto
entrypoint now builds it from PG_USER/PG_PASSWORD/PG_HOST using
node's encodeURIComponent for URL-safe encoding.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 16:33:43 +02:00
hsiegeln
a9aee77077 fix: make get-cameleer.ps1 safe under irm | iex
iex evaluates the script body in the caller's scope, so top-level
side effects leak into the user's interactive PowerShell. Two leaks
fixed:

- Dropped 'exit $LASTEXITCODE' — would close the user's shell window.
- Dropped 'Set-Location $Dir' — would leave the user sitting in
  .\installer\ after the run. install.ps1 uses $PSScriptRoot to find
  its templates, so it can be invoked from any CWD via its full path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 15:44:01 +02:00
hsiegeln
0b092065c5 feat: bootstrap scripts auto-launch the installer
get-cameleer.sh and get-cameleer.ps1 now download the installer files
and exec install.sh / install.ps1 immediately instead of just printing
a "run this next" hint. Extra arguments are forwarded to the installer.

PowerShell bootstrap fetches install.ps1 (not install.sh) so Windows
users no longer need bash. README updated to use the bash -c "$(curl ...)"
form so install.sh's interactive prompts inherit the user's TTY.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 15:35:32 +02:00
hsiegeln
528c6d1980 feat: restore PowerShell installer lost during submodule migration
install.ps1 was deleted in the refactor that moved the installer to its
own repo but was never copied over. Restored from cameleer-saas history
with registry default updated to registry.cameleer.io.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 15:31:54 +02:00
hsiegeln
ffd817aecc chore: use registry.cameleer.io as default image registry
Public-facing registry URL for customer deployments.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 15:23:40 +02:00
6bba20d887 docs: add CLAUDE.md for AI-assisted development context 2026-04-25 13:04:37 +02:00
b62a14b1d5 docs: comprehensive README with all installer settings 2026-04-25 13:04:10 +02:00
10 changed files with 1953 additions and 133 deletions

52
CLAUDE.md Normal file
View File

@@ -0,0 +1,52 @@
# Installer
## Deployment Modes
The installer (`install.sh`) supports two deployment modes:
| | Multi-tenant SaaS (`DEPLOYMENT_MODE=saas`) | Standalone (`DEPLOYMENT_MODE=standalone`) |
|---|---|---|
| **Containers** | traefik, postgres, clickhouse, logto, cameleer-saas | traefik, postgres, clickhouse, server, server-ui |
| **Auth** | Logto OIDC (SaaS admin + tenant users) | Local auth (built-in admin, no identity provider) |
| **Tenant management** | SaaS admin creates/manages tenants via UI | Single server instance, no fleet management |
| **PostgreSQL** | `cameleer-postgres` image (multi-DB init) | Stock `postgres:16-alpine` (server creates schema via Flyway) |
| **Use case** | Platform vendor managing multiple customers | Single customer running the product directly |
Standalone mode generates a simpler compose with the server running directly. No Logto, no SaaS management plane, no bootstrap. The admin logs in with local credentials at `/`.
## Compose templates
The installer uses static docker-compose templates in `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
## SMTP configuration
SMTP / email connector configuration has been moved from the installer to the SaaS vendor admin UI (Email Connector page at `/vendor/email`). The installer no longer prompts for or persists SMTP settings.
Previously, SMTP env vars (`SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASS`, `SMTP_FROM_EMAIL`) were passed to the `cameleer-logto` container and configured via the bootstrap script. This one-shot approach was fragile — email delivery is now configured at runtime through the Logto Management API.
## Registry configuration
The installer supports pulling images from a custom Docker registry via `--registry`. Default: `gitea.siegeln.net/cameleer`.
When a registry is configured, the installer writes `*_IMAGE` env vars to `.env` (e.g. `TRAEFIK_IMAGE`, `POSTGRES_IMAGE`, `CAMELEER_IMAGE`) which override the defaults baked into the compose templates. In SaaS mode, provisioning image refs (`CAMELEER_SAAS_PROVISIONING_*IMAGE`) are also set from the registry.
For private registries, pass `--registry-user` / `--registry-token`. The installer runs `docker login` before pulling images. Credentials are persisted in `cameleer.conf` for upgrades/reconfigure.
## Env var naming convention
- `CAMELEER_AGENT_*` — agent config (consumed by the Java agent)
- `CAMELEER_SERVER_*` — server config (consumed by cameleer-server)
- `CAMELEER_SAAS_*` — SaaS management plane config
- `CAMELEER_SAAS_PROVISIONING_*` — "SaaS forwards this to provisioned tenant servers"
- No prefix (e.g. `POSTGRES_PASSWORD`, `PUBLIC_HOST`) — shared infrastructure, consumed by multiple components
## Development
This repo is used as a git submodule in `cameleer-saas` at `installer/`. The dev compose in `cameleer-saas` chains the production templates from this repo via `COMPOSE_FILE` — no duplication.
Fixes to compose templates go here, then `git submodule update --remote installer` in cameleer-saas propagates them to dev.

253
README.md
View File

@@ -1,3 +1,252 @@
# cameleer-saas-installer
# Cameleer SaaS Installer
One-line installer for the Cameleer SaaS platform
One-line installer for the [Cameleer](https://cameleer.io) observability platform. Deploys as Docker containers behind Traefik with automatic TLS, Logto OIDC, and multi-tenant provisioning.
## Quick Start
**Linux / macOS:**
```bash
bash -c "$(curl -fsSL https://registry.cameleer.io/cameleer/cameleer-saas-installer/raw/branch/main/get-cameleer.sh)"
```
**Windows (PowerShell):**
```powershell
irm https://registry.cameleer.io/cameleer/cameleer-saas-installer/raw/branch/main/get-cameleer.ps1 | iex
```
The bootstrap downloads the installer into `./installer/` and launches it immediately. The interactive prompts run in your terminal.
**Pin a version:**
```bash
bash -c "$(curl -fsSL .../get-cameleer.sh)" -- --version=v1.0.0
```
```powershell
& ([scriptblock]::Create((irm .../get-cameleer.ps1))) -Version v1.0.0
```
Any extra arguments are forwarded to `install.sh` / `install.ps1` (e.g. `--silent`, `--expert`, `--public-host=…`).
## Deployment Modes
| | Multi-tenant SaaS | Standalone |
|---|---|---|
| **Containers** | Traefik, PostgreSQL, ClickHouse, Logto, Cameleer SaaS | Traefik, PostgreSQL, ClickHouse, Server, Server UI |
| **Auth** | Logto OIDC (platform admin + tenant users) | Local auth (built-in admin, no IdP) |
| **Tenants** | Create/manage multiple tenants via UI | Single server instance |
| **Use case** | Platform vendor managing customers | Single customer running the product |
## Installation Modes
| Mode | Flag | Description |
|------|------|-------------|
| Simple | *(default)* | 6 questions, sensible defaults |
| Expert | `--expert` | Configure everything |
| Silent | `--silent` | Fully automated, all values from CLI/env/config |
## Upgrade / Reconfigure
Re-running the installer on an existing installation presents a menu:
```
[1] Upgrade — pull new images, update compose, restart
[2] Reconfigure — re-run interactive setup, preserve data
[3] Reinstall — fresh install (destroys data volumes)
[4] Cancel
```
---
## Configuration Reference
Settings can be provided via CLI flags, environment variables, config file (`cameleer.conf`), or interactive prompts. Priority (highest to lowest):
1. CLI arguments
2. Environment variables
3. Config file (via `--config` or auto-detected)
4. Interactive prompts
5. Default values
### Hostname & Protocol
| Setting | CLI Flag | Env Var | Config Key | Default |
|---------|----------|---------|------------|---------|
| Public hostname | `--public-host` | `PUBLIC_HOST` | `public_host` | auto-detected |
| Auth hostname | `--auth-host` | `AUTH_HOST` | `auth_host` | same as `PUBLIC_HOST` |
| Protocol | `--public-protocol` | `PUBLIC_PROTOCOL` | `public_protocol` | `https` |
`PUBLIC_HOST` is the primary hostname for the platform. `AUTH_HOST` defaults to the same value (single-domain setup). Set a separate `AUTH_HOST` only if Logto runs on a dedicated domain.
### Admin Credentials
| Setting | CLI Flag | Env Var | Config Key | Default |
|---------|----------|---------|------------|---------|
| Admin login | `--admin-user` | `SAAS_ADMIN_USER` | `admin_user` | `admin` (standalone) / `admin@<PUBLIC_HOST>` (SaaS) |
| Admin password | `--admin-password` | `SAAS_ADMIN_PASS` | `admin_password` | auto-generated |
In SaaS mode, `SAAS_ADMIN_USER` must be an email address — it is used as both the Logto username and primaryEmail. The installer validates email format in SaaS mode and auto-appends `@<PUBLIC_HOST>` if the `@` is missing. In standalone mode, any username is accepted.
In standalone mode, the env vars are `SERVER_ADMIN_USER` / `SERVER_ADMIN_PASS`.
### TLS Certificates
| Setting | CLI Flag | Env Var | Config Key | Default |
|---------|----------|---------|------------|---------|
| TLS mode | `--tls-mode` | `TLS_MODE` | `tls_mode` | `self-signed` |
| Certificate file | `--cert-file` | `CERT_FILE` | `cert_file` | — |
| Private key file | `--key-file` | `KEY_FILE` | `key_file` | — |
| CA bundle | `--ca-file` | `CA_FILE` | `ca_file` | — |
Set `--tls-mode=custom` and provide PEM-encoded certificate files. With `self-signed`, a certificate is auto-generated at install time.
### Network Ports
| Setting | CLI Flag | Env Var | Config Key | Default |
|---------|----------|---------|------------|---------|
| HTTP port | `--http-port` | `HTTP_PORT` | `http_port` | `80` |
| HTTPS port | `--https-port` | `HTTPS_PORT` | `https_port` | `443` |
| Logto console port | `--logto-console-port` | `LOGTO_CONSOLE_PORT` | `logto_console_port` | `3002` |
| Logto console exposed | `--logto-console-exposed` | `LOGTO_CONSOLE_EXPOSED` | `logto_console_exposed` | `true` |
HTTP traffic is auto-redirected to HTTPS. The Logto admin console is bound to `127.0.0.1` by default (localhost only). Set `--logto-console-exposed` to bind on all interfaces.
### Database Passwords
| Setting | CLI Flag | Env Var | Config Key | Default |
|---------|----------|---------|------------|---------|
| PostgreSQL password | `--postgres-password` | `POSTGRES_PASSWORD` | `postgres_password` | auto-generated |
| ClickHouse password | `--clickhouse-password` | `CLICKHOUSE_PASSWORD` | `clickhouse_password` | auto-generated |
Auto-generated passwords are stored in `credentials.txt` inside the install directory.
### Docker
| Setting | CLI Flag | Env Var | Config Key | Default |
|---------|----------|---------|------------|---------|
| Docker socket | `--docker-socket` | `DOCKER_SOCKET` | `docker_socket` | `/var/run/docker.sock` |
| Compose project name | `--compose-project` | `COMPOSE_PROJECT` | `compose_project` | `cameleer-saas` |
The Docker socket is required for tenant provisioning (SaaS mode) — the platform creates per-tenant server containers on demand.
### Image Registry
| Setting | CLI Flag | Env Var | Config Key | Default |
|---------|----------|---------|------------|---------|
| Registry | `--registry` | `REGISTRY` | `registry` | `gitea.siegeln.net/cameleer` |
| Registry username | `--registry-user` | `REGISTRY_USER` | `registry_user` | — |
| Registry token | `--registry-token` | `REGISTRY_TOKEN` | `registry_token` | — |
| Image version | `--version` | `VERSION` | `version` | `latest` |
For private registries, provide credentials and the installer runs `docker login` before pulling. The registry prefix is applied to all container images.
### Email / SMTP
Email connector configuration (SMTP, SES, etc.) is managed at runtime via the vendor admin UI at `/vendor/email`. The installer does not configure email delivery.
Self-service registration is disabled by default and is enabled automatically when the admin configures an email connector.
### Monitoring
| Setting | CLI Flag | Env Var | Config Key | Default |
|---------|----------|---------|------------|---------|
| Monitoring network | `--monitoring-network` | `MONITORING_NETWORK` | `monitoring_network` | — |
Specify an external Docker network name to attach containers for Prometheus scraping. Containers expose metrics via `prometheus.io/*` labels.
### TLS Verification
| Setting | CLI Flag | Env Var | Config Key | Default |
|---------|----------|---------|------------|---------|
| Node TLS reject | `--node-tls-reject` | `NODE_TLS_REJECT` | `node_tls_reject` | `0` (self-signed) / `1` (custom) |
Controls `NODE_TLS_REJECT_UNAUTHORIZED` inside the Logto container. Set to `0` for self-signed certificates, `1` for production certificates from a trusted CA.
---
## Auto-Generated Secrets
These are generated automatically and never need to be set manually:
| Secret | Env Var | Description |
|--------|---------|-------------|
| JWT signing secret | `CAMELEER_SERVER_SECURITY_JWTSECRET` | Shared secret for JWT token signing across provisioned tenant servers |
| Bootstrap token | `BOOTSTRAP_TOKEN` | Server initialization token (standalone mode only) |
---
## Architecture
```
┌─────────────────────────────────────┐
│ Traefik :443 │
│ TLS termination + path routing │
└──────┬──────────────┬───────────────┘
│ │
/platform/* │ │ /* (catch-all)
│ │
┌──────▼──────┐ ┌─────▼──────────┐
│ Cameleer │ │ Logto │
│ SaaS :8080 │ │ OIDC :3001 │
│ Vendor + │ │ Custom sign-in │
│ Tenant UI │ │ Admin :3002 │
└──────┬─────┘ └─────────────────┘
Docker API │ provisions per-tenant
┌──────▼──────────────────────┐
│ /t/{slug}/* │
│ cameleer-server + server-ui │
│ (one pair per tenant) │
└─────────────────────────────┘
```
All services share a single hostname. Routing:
| Path | Target |
|------|--------|
| `/platform/*` | Cameleer SaaS management plane |
| `/t/{slug}/*` | Per-tenant server dashboard |
| `/*` | Logto (sign-in, OIDC, experience API) |
| `/` | Redirect to `/platform/` |
---
## Files
| File | Purpose |
|------|---------|
| `get-cameleer.sh` | Bootstrap script (bash) — downloads installer files and launches `install.sh` |
| `get-cameleer.ps1` | Bootstrap script (PowerShell) — downloads installer files and launches `install.ps1` |
| `install.sh` | Main installer (Linux / macOS) — interactive or silent deployment |
| `install.ps1` | Main installer (Windows PowerShell) — interactive or silent deployment |
| `templates/docker-compose.yml` | Base infrastructure (Traefik, PostgreSQL, ClickHouse) |
| `templates/docker-compose.saas.yml` | SaaS mode (Logto + management plane) |
| `templates/docker-compose.server.yml` | Standalone mode (server + UI) |
| `templates/docker-compose.tls.yml` | Overlay: custom TLS certificate volume |
| `templates/docker-compose.monitoring.yml` | Overlay: external monitoring network |
| `templates/traefik-dynamic.yml` | Traefik TLS certificate configuration |
| `templates/.env.example` | Documented environment variable template |
---
## Silent Install Example
```bash
./install.sh --silent \
--public-host=app.example.com \
--admin-user=admin \
--admin-password=s3cret \
--tls-mode=custom \
--cert-file=/etc/ssl/cert.pem \
--key-file=/etc/ssl/key.pem \
--registry=registry.example.com/cameleer \
--registry-user=deploy \
--registry-token=ghp_xxx
```
## License
Proprietary. See [cameleer.io](https://cameleer.io) for licensing.

View File

@@ -4,12 +4,13 @@
Bootstrap script — downloads the Cameleer installer and runs it.
.EXAMPLE
irm https://registry.cameleer.io/cameleer/cameleer-saas-installer/raw/branch/main/get-cameleer.ps1 | iex
.\get-cameleer.ps1 -Version v1.2.0
& ([scriptblock]::Create((irm https://.../get-cameleer.ps1))) -Version v1.2.0
#>
param(
[string]$Version,
[string]$Ref,
[switch]$Run
[Parameter(ValueFromRemainingArguments = $true)]
[string[]]$InstallerArgs
)
$ErrorActionPreference = 'Stop'
@@ -23,7 +24,7 @@ $Base = "$Repo/$RefPath"
$Dir = '.\installer'
$Files = @(
'install.sh'
'install.ps1'
'templates/docker-compose.yml'
'templates/docker-compose.saas.yml'
'templates/docker-compose.server.yml'
@@ -47,11 +48,12 @@ foreach ($file in $Files) {
}
Write-Host ''
Write-Host "Installer ready in $Dir\"
Write-Host 'Run: cd installer; .\install.sh'
Write-Host "Installer downloaded to $Dir\ — launching..."
Write-Host ''
if ($Run) {
Set-Location $Dir
& .\install.sh @args
$installerPath = Join-Path $Dir 'install.ps1'
if ($InstallerArgs) {
& $installerPath @InstallerArgs
} else {
& $installerPath
}

View File

@@ -3,18 +3,20 @@ set -euo pipefail
# Bootstrap script — downloads the Cameleer installer and runs it.
# Usage:
# curl -fsSL https://get.cameleer.io/install | bash
# curl -fsSL https://get.cameleer.io/install | bash -s -- --version v1.2.0
# bash -c "$(curl -fsSL https://get.cameleer.io/install)"
# bash -c "$(curl -fsSL https://get.cameleer.io/install)" -- --version v1.2.0
REPO="https://registry.cameleer.io/cameleer/cameleer-saas-installer/raw"
REF="branch/main"
DIR="./installer"
# Parse --version / --ref
# Parse --version / --ref (consume them; remaining args are forwarded to install.sh)
PASS_ARGS=()
for arg in "$@"; do
case "$arg" in
--version=*) REF="tag/${arg#*=}"; shift ;;
--ref=*) REF="branch/${arg#*=}"; shift ;;
--version=*) REF="tag/${arg#*=}" ;;
--ref=*) REF="branch/${arg#*=}" ;;
*) PASS_ARGS+=("$arg") ;;
esac
done
@@ -43,13 +45,8 @@ done
chmod +x "$DIR/install.sh"
echo ""
echo "Installer ready in $DIR/"
echo "Run: cd $DIR && ./install.sh"
echo "Installer downloaded to $DIR/ — launching..."
echo ""
# Auto-run if not piped with extra args that look like they want manual control
if [ "${1:-}" = "--run" ]; then
shift
cd "$DIR"
exec ./install.sh "$@"
fi
cd "$DIR"
exec ./install.sh "${PASS_ARGS[@]}"

1538
install.ps1 Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@ set -euo pipefail
CAMELEER_INSTALLER_VERSION="1.0.0"
CAMELEER_DEFAULT_VERSION="latest"
DEFAULT_REGISTRY="gitea.siegeln.net/cameleer"
DEFAULT_REGISTRY="registry.cameleer.io/cameleer"
# --- Colors ---
RED='\033[0;31m'
@@ -46,11 +46,6 @@ _ENV_COMPOSE_PROJECT="${COMPOSE_PROJECT:-}"
_ENV_DOCKER_SOCKET="${DOCKER_SOCKET:-}"
_ENV_NODE_TLS_REJECT="${NODE_TLS_REJECT:-}"
_ENV_DEPLOYMENT_MODE="${DEPLOYMENT_MODE:-}"
_ENV_SMTP_HOST="${SMTP_HOST:-}"
_ENV_SMTP_PORT="${SMTP_PORT:-}"
_ENV_SMTP_USER="${SMTP_USER:-}"
_ENV_SMTP_PASS="${SMTP_PASS:-}"
_ENV_SMTP_FROM_EMAIL="${SMTP_FROM_EMAIL:-}"
_ENV_REGISTRY="${REGISTRY:-}"
_ENV_REGISTRY_USER="${REGISTRY_USER:-}"
_ENV_REGISTRY_TOKEN="${REGISTRY_TOKEN:-}"
@@ -61,6 +56,7 @@ AUTH_HOST=""
PUBLIC_PROTOCOL=""
ADMIN_USER=""
ADMIN_PASS=""
TLS_MODE=""
CERT_FILE=""
KEY_FILE=""
@@ -77,11 +73,6 @@ COMPOSE_PROJECT=""
DOCKER_SOCKET=""
NODE_TLS_REJECT=""
DEPLOYMENT_MODE=""
SMTP_HOST=""
SMTP_PORT=""
SMTP_USER=""
SMTP_PASS=""
SMTP_FROM_EMAIL=""
REGISTRY=""
REGISTRY_USER=""
REGISTRY_TOKEN=""
@@ -156,6 +147,14 @@ generate_password() {
tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 32 || :
}
# Write KEY='escaped_value' to a file. Single-quotes prevent Docker Compose
# from interpreting $, &, ;, etc. Embedded single quotes are escaped as '\''.
env_val() {
local file="$1" key="$2" val="$3"
val="${val//\'/\'\\\'\'}"
printf "%s='%s'\n" "$key" "$val" >> "$file"
}
# --- Argument parsing ---
parse_args() {
@@ -170,6 +169,7 @@ parse_args() {
--public-protocol) PUBLIC_PROTOCOL="$2"; shift ;;
--admin-user) ADMIN_USER="$2"; shift ;;
--admin-password) ADMIN_PASS="$2"; shift ;;
--tls-mode) TLS_MODE="$2"; shift ;;
--cert-file) CERT_FILE="$2"; shift ;;
--key-file) KEY_FILE="$2"; shift ;;
@@ -186,11 +186,6 @@ parse_args() {
--docker-socket) DOCKER_SOCKET="$2"; shift ;;
--node-tls-reject) NODE_TLS_REJECT="$2"; shift ;;
--deployment-mode) DEPLOYMENT_MODE="$2"; shift ;;
--smtp-host) SMTP_HOST="$2"; shift ;;
--smtp-port) SMTP_PORT="$2"; shift ;;
--smtp-user) SMTP_USER="$2"; shift ;;
--smtp-pass) SMTP_PASS="$2"; shift ;;
--smtp-from-email) SMTP_FROM_EMAIL="$2"; shift ;;
--registry) REGISTRY="$2"; shift ;;
--registry-user) REGISTRY_USER="$2"; shift ;;
--registry-token) REGISTRY_TOKEN="$2"; shift ;;
@@ -234,7 +229,7 @@ show_help() {
echo " --help Show this help"
echo ""
echo "Registry options:"
echo " --registry REGISTRY Image registry (default: gitea.siegeln.net/cameleer)"
echo " --registry REGISTRY Image registry (default: registry.cameleer.io/cameleer)"
echo " --registry-user USER Registry username for docker login"
echo " --registry-token TOKEN Registry token/password for docker login"
echo ""
@@ -259,8 +254,9 @@ load_config_file() {
case "$key" in
\#*|"") continue ;;
esac
key=$(echo "$key" | tr -d ' ')
value=$(echo "$value" | sed 's/^[ ]*//;s/[ ]*$//')
key=$(printf '%s' "$key" | tr -d ' ')
# Trim whitespace, strip surrounding quotes, unescape '\''
value=$(printf '%s' "$value" | sed "s/^[ ]*//;s/[ ]*$//;s/^'\\(.*\\)'$/\\1/;s/^\"\\(.*\\)\"$/\\1/" | sed "s/'\\\\''/'/g")
case "$key" in
install_dir) [ -z "$INSTALL_DIR" ] && INSTALL_DIR="$value" ;;
public_host) [ -z "$PUBLIC_HOST" ] && PUBLIC_HOST="$value" ;;
@@ -268,6 +264,7 @@ load_config_file() {
public_protocol) [ -z "$PUBLIC_PROTOCOL" ] && PUBLIC_PROTOCOL="$value" ;;
admin_user) [ -z "$ADMIN_USER" ] && ADMIN_USER="$value" ;;
admin_password) [ -z "$ADMIN_PASS" ] && ADMIN_PASS="$value" ;;
tls_mode) [ -z "$TLS_MODE" ] && TLS_MODE="$value" ;;
cert_file) [ -z "$CERT_FILE" ] && CERT_FILE="$value" ;;
key_file) [ -z "$KEY_FILE" ] && KEY_FILE="$value" ;;
@@ -284,11 +281,6 @@ load_config_file() {
docker_socket) [ -z "$DOCKER_SOCKET" ] && DOCKER_SOCKET="$value" ;;
node_tls_reject) [ -z "$NODE_TLS_REJECT" ] && NODE_TLS_REJECT="$value" ;;
deployment_mode) [ -z "$DEPLOYMENT_MODE" ] && DEPLOYMENT_MODE="$value" ;;
smtp_host) [ -z "$SMTP_HOST" ] && SMTP_HOST="$value" ;;
smtp_port) [ -z "$SMTP_PORT" ] && SMTP_PORT="$value" ;;
smtp_user) [ -z "$SMTP_USER" ] && SMTP_USER="$value" ;;
smtp_pass) [ -z "$SMTP_PASS" ] && SMTP_PASS="$value" ;;
smtp_from_email) [ -z "$SMTP_FROM_EMAIL" ] && SMTP_FROM_EMAIL="$value" ;;
registry) [ -z "$REGISTRY" ] && REGISTRY="$value" ;;
registry_user) [ -z "$REGISTRY_USER" ] && REGISTRY_USER="$value" ;;
registry_token) [ -z "$REGISTRY_TOKEN" ] && REGISTRY_TOKEN="$value" ;;
@@ -303,6 +295,7 @@ load_env_overrides() {
[ -z "$PUBLIC_PROTOCOL" ] && PUBLIC_PROTOCOL="$_ENV_PUBLIC_PROTOCOL"
[ -z "$ADMIN_USER" ] && ADMIN_USER="${SAAS_ADMIN_USER:-}"
[ -z "$ADMIN_PASS" ] && ADMIN_PASS="${SAAS_ADMIN_PASS:-}"
[ -z "$TLS_MODE" ] && TLS_MODE="$_ENV_TLS_MODE"
[ -z "$CERT_FILE" ] && CERT_FILE="$_ENV_CERT_FILE"
[ -z "$KEY_FILE" ] && KEY_FILE="$_ENV_KEY_FILE"
@@ -319,11 +312,6 @@ load_env_overrides() {
[ -z "$DOCKER_SOCKET" ] && DOCKER_SOCKET="$_ENV_DOCKER_SOCKET"
[ -z "$NODE_TLS_REJECT" ] && NODE_TLS_REJECT="$_ENV_NODE_TLS_REJECT"
[ -z "$DEPLOYMENT_MODE" ] && DEPLOYMENT_MODE="$_ENV_DEPLOYMENT_MODE"
[ -z "$SMTP_HOST" ] && SMTP_HOST="$_ENV_SMTP_HOST"
[ -z "$SMTP_PORT" ] && SMTP_PORT="$_ENV_SMTP_PORT"
[ -z "$SMTP_USER" ] && SMTP_USER="$_ENV_SMTP_USER"
[ -z "$SMTP_PASS" ] && SMTP_PASS="$_ENV_SMTP_PASS"
[ -z "$SMTP_FROM_EMAIL" ] && SMTP_FROM_EMAIL="$_ENV_SMTP_FROM_EMAIL"
[ -z "$REGISTRY" ] && REGISTRY="$_ENV_REGISTRY"
[ -z "$REGISTRY_USER" ] && REGISTRY_USER="$_ENV_REGISTRY_USER"
[ -z "$REGISTRY_TOKEN" ] && REGISTRY_TOKEN="$_ENV_REGISTRY_TOKEN"
@@ -441,7 +429,37 @@ run_simple_prompts() {
prompt INSTALL_DIR "Install directory" "${INSTALL_DIR:-$DEFAULT_INSTALL_DIR}"
prompt PUBLIC_HOST "Public hostname" "${PUBLIC_HOST:-localhost}"
prompt ADMIN_USER "Admin username" "${ADMIN_USER:-$DEFAULT_ADMIN_USER}"
echo ""
echo " Deployment mode:"
echo " [1] Multi-tenant SaaS — manage platform, provision tenants on demand"
echo " [2] Single-tenant — one server instance, local auth, no identity provider"
echo ""
local deploy_choice
read -rp " Select mode [1]: " deploy_choice
case "${deploy_choice:-1}" in
2)
DEPLOYMENT_MODE="standalone"
;;
*)
DEPLOYMENT_MODE="saas"
;;
esac
echo ""
if [ "$DEPLOYMENT_MODE" = "saas" ]; then
while true; do
prompt ADMIN_USER "Admin email" "${ADMIN_USER:-admin@${PUBLIC_HOST:-localhost}}"
# Validate email: must be user@domain.tld format
if echo "$ADMIN_USER" | grep -qE '^[^@]+@[^@]+\.[^@]+$'; then
break
fi
echo -e " ${RED}Invalid email address.${NC} Must be a valid email (e.g. admin@company.com)."
ADMIN_USER=""
done
else
prompt ADMIN_USER "Admin username" "${ADMIN_USER:-$DEFAULT_ADMIN_USER}"
fi
if prompt_yesno "Auto-generate admin password?" "y"; then
ADMIN_PASS=""
@@ -471,33 +489,6 @@ run_simple_prompts() {
prompt_password REGISTRY_TOKEN "Registry token/password" "${REGISTRY_TOKEN:-}"
fi
echo ""
echo " Deployment mode:"
echo " [1] Multi-tenant SaaS — manage platform, provision tenants on demand"
echo " [2] Single-tenant — one server instance, local auth, no identity provider"
echo ""
local deploy_choice
read -rp " Select mode [1]: " deploy_choice
case "${deploy_choice:-1}" in
2)
DEPLOYMENT_MODE="standalone"
;;
*)
DEPLOYMENT_MODE="saas"
;;
esac
# SMTP for email verification (SaaS mode only)
if [ "$DEPLOYMENT_MODE" = "saas" ]; then
echo ""
if prompt_yesno "Configure SMTP for email verification? (required for self-service sign-up)"; then
prompt SMTP_HOST "SMTP host" "${SMTP_HOST:-}"
prompt SMTP_PORT "SMTP port" "${SMTP_PORT:-587}"
prompt SMTP_USER "SMTP username" "${SMTP_USER:-}"
prompt_password SMTP_PASS "SMTP password" "${SMTP_PASS:-}"
prompt SMTP_FROM_EMAIL "From email address" "${SMTP_FROM_EMAIL:-noreply@${PUBLIC_HOST}}"
fi
fi
}
run_expert_prompts() {
@@ -549,7 +540,16 @@ merge_config() {
: "${INSTALL_DIR:=$DEFAULT_INSTALL_DIR}"
: "${PUBLIC_HOST:=localhost}"
: "${PUBLIC_PROTOCOL:=$DEFAULT_PUBLIC_PROTOCOL}"
: "${ADMIN_USER:=$DEFAULT_ADMIN_USER}"
if [ "$DEPLOYMENT_MODE" = "saas" ]; then
: "${ADMIN_USER:=admin@${PUBLIC_HOST}}"
# Validate email format in SaaS mode
if ! echo "$ADMIN_USER" | grep -qE '^[^@]+@[^@]+\.[^@]+$'; then
echo -e "${RED}ERROR:${NC} SAAS_ADMIN_USER must be a valid email address (e.g. admin@company.com), got: '$ADMIN_USER'" >&2
exit 1
fi
else
: "${ADMIN_USER:=$DEFAULT_ADMIN_USER}"
fi
: "${TLS_MODE:=$DEFAULT_TLS_MODE}"
: "${HTTP_PORT:=$DEFAULT_HTTP_PORT}"
: "${HTTPS_PORT:=$DEFAULT_HTTPS_PORT}"
@@ -624,6 +624,7 @@ generate_passwords() {
ADMIN_PASS=$(generate_password)
log_info "Generated admin password."
fi
if [ -z "$POSTGRES_PASSWORD" ]; then
POSTGRES_PASSWORD=$(generate_password)
log_info "Generated PostgreSQL password."
@@ -663,15 +664,10 @@ HTTPS_PORT=${HTTPS_PORT}
# PostgreSQL
POSTGRES_USER=cameleer
POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
POSTGRES_DB=cameleer
# ClickHouse
CLICKHOUSE_PASSWORD=${CLICKHOUSE_PASSWORD}
# Server admin
SERVER_ADMIN_USER=${ADMIN_USER}
SERVER_ADMIN_PASS=${ADMIN_PASS}
# Bootstrap token (required by server, not used externally in standalone mode)
BOOTSTRAP_TOKEN=$(generate_password)
@@ -694,6 +690,10 @@ SERVER_UI_IMAGE=${REGISTRY}/cameleer-server-ui
# 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
# Passwords are appended with single-quoting to handle special characters
env_val "$f" POSTGRES_PASSWORD "$POSTGRES_PASSWORD"
env_val "$f" CLICKHOUSE_PASSWORD "$CLICKHOUSE_PASSWORD"
env_val "$f" SERVER_ADMIN_PASS "$ADMIN_PASS"
if [ "$TLS_MODE" = "custom" ]; then
echo "CERT_FILE=/user-certs/cert.pem" >> "$f"
echo "KEY_FILE=/user-certs/key.pem" >> "$f"
@@ -729,15 +729,10 @@ LOGTO_CONSOLE_BIND=$([ "$LOGTO_CONSOLE_EXPOSED" = "true" ] && echo "0.0.0.0" ||
# PostgreSQL
POSTGRES_USER=cameleer
POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
POSTGRES_DB=cameleer_saas
# ClickHouse
CLICKHOUSE_PASSWORD=${CLICKHOUSE_PASSWORD}
# Admin user
SAAS_ADMIN_USER=${ADMIN_USER}
SAAS_ADMIN_PASS=${ADMIN_PASS}
# TLS
NODE_TLS_REJECT=${NODE_TLS_REJECT}
@@ -774,17 +769,15 @@ CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE=${REGISTRY}/cameleer-runtime-base:${
# JWT signing secret (forwarded to provisioned tenant servers, must be non-empty)
CAMELEER_SERVER_SECURITY_JWTSECRET=$(generate_password)
# SMTP (for email verification during registration)
SMTP_HOST=${SMTP_HOST}
SMTP_PORT=${SMTP_PORT:-587}
SMTP_USER=${SMTP_USER}
SMTP_PASS=${SMTP_PASS}
SMTP_FROM_EMAIL=${SMTP_FROM_EMAIL:-noreply@${PUBLIC_HOST}}
# 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")
EOF
# Passwords are appended with single-quoting to handle special characters
env_val "$f" POSTGRES_PASSWORD "$POSTGRES_PASSWORD"
env_val "$f" CLICKHOUSE_PASSWORD "$CLICKHOUSE_PASSWORD"
env_val "$f" SAAS_ADMIN_PASS "$ADMIN_PASS"
if [ -n "$MONITORING_NETWORK" ]; then
echo "" >> "$f"
echo "# Monitoring" >> "$f"
@@ -964,15 +957,11 @@ compose_project=${COMPOSE_PROJECT}
docker_socket=${DOCKER_SOCKET}
node_tls_reject=${NODE_TLS_REJECT}
deployment_mode=${DEPLOYMENT_MODE}
smtp_host=${SMTP_HOST}
smtp_port=${SMTP_PORT}
smtp_user=${SMTP_USER}
smtp_pass=${SMTP_PASS}
smtp_from_email=${SMTP_FROM_EMAIL}
registry=${REGISTRY}
registry_user=${REGISTRY_USER}
registry_token=${REGISTRY_TOKEN}
EOF
# Passwords appended with single-quoting for special character safety
env_val "$f" registry_token "$REGISTRY_TOKEN"
log_info "Saved installer config to cameleer.conf"
}

View File

@@ -50,7 +50,9 @@ CLICKHOUSE_PASSWORD=CHANGE_ME
# ============================================================
# Admin credentials (SaaS mode)
# ============================================================
SAAS_ADMIN_USER=admin
# In SaaS mode, this must be an email address (primary user identity).
# In standalone mode, any username is accepted.
SAAS_ADMIN_USER=admin@example.com
SAAS_ADMIN_PASS=CHANGE_ME
# ============================================================
@@ -61,14 +63,10 @@ SAAS_ADMIN_PASS=CHANGE_ME
# BOOTSTRAP_TOKEN=CHANGE_ME
# ============================================================
# SMTP (for email verification during registration)
# Email / SMTP
# ============================================================
# Required for self-service sign-up. Without SMTP, only admin-created users can sign in.
SMTP_HOST=
SMTP_PORT=587
SMTP_USER=
SMTP_PASS=
SMTP_FROM_EMAIL=noreply@cameleer.io
# Email connector configuration is managed at runtime via the vendor
# admin UI (Email Connector page at /vendor/email). No SMTP env vars needed.
# ============================================================
# TLS
@@ -90,9 +88,9 @@ 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
# CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE=gitea.siegeln.net/cameleer/cameleer-runtime-base:latest
# CAMELEER_SAAS_PROVISIONING_SERVERIMAGE=registry.cameleer.io/cameleer/cameleer-server:latest
# CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE=registry.cameleer.io/cameleer/cameleer-server-ui:latest
# CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE=registry.cameleer.io/cameleer/cameleer-runtime-base:latest
# ============================================================
# Monitoring (optional)

View File

@@ -3,13 +3,14 @@
services:
cameleer-logto:
image: ${LOGTO_IMAGE:-gitea.siegeln.net/cameleer/cameleer-logto}:${VERSION:-latest}
image: ${LOGTO_IMAGE:-registry.cameleer.io/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
# DB_URL is built by the entrypoint from PG_USER/PG_PASSWORD/PG_HOST
# to safely handle special characters in the password
ENDPOINT: ${PUBLIC_PROTOCOL:-https}://${AUTH_HOST:-localhost}
ADMIN_ENDPOINT: ${PUBLIC_PROTOCOL:-https}://${AUTH_HOST:-localhost}:${LOGTO_CONSOLE_PORT:-3002}
TRUST_PROXY_HEADER: 1
@@ -26,12 +27,6 @@ services:
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}
# SMTP (for email verification during registration)
SMTP_HOST: ${SMTP_HOST:-}
SMTP_PORT: ${SMTP_PORT:-587}
SMTP_USER: ${SMTP_USER:-}
SMTP_PASS: ${SMTP_PASS:-}
SMTP_FROM_EMAIL: ${SMTP_FROM_EMAIL:-noreply@cameleer.io}
extra_hosts:
# Logto validates M2M tokens by fetching its own JWKS from ENDPOINT.
# Route the public hostname back to the Docker host (Traefik on :443)
@@ -68,7 +63,7 @@ services:
- monitoring
cameleer-saas:
image: ${CAMELEER_IMAGE:-gitea.siegeln.net/cameleer/cameleer-saas}:${VERSION:-latest}
image: ${CAMELEER_IMAGE:-registry.cameleer.io/cameleer/cameleer-saas}:${VERSION:-latest}
restart: unless-stopped
depends_on:
cameleer-logto:
@@ -91,9 +86,9 @@ services:
CAMELEER_SAAS_PROVISIONING_DATASOURCEPASSWORD: ${POSTGRES_PASSWORD}
CAMELEER_SAAS_PROVISIONING_CLICKHOUSEPASSWORD: ${CLICKHOUSE_PASSWORD}
CAMELEER_SERVER_SECURITY_JWTSECRET: ${CAMELEER_SERVER_SECURITY_JWTSECRET:?CAMELEER_SERVER_SECURITY_JWTSECRET must be set in .env}
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}
CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE: ${CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE:-gitea.siegeln.net/cameleer/cameleer-runtime-base:latest}
CAMELEER_SAAS_PROVISIONING_SERVERIMAGE: ${CAMELEER_SAAS_PROVISIONING_SERVERIMAGE:-registry.cameleer.io/cameleer/cameleer-server:latest}
CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE: ${CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE:-registry.cameleer.io/cameleer/cameleer-server-ui:latest}
CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE: ${CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE:-registry.cameleer.io/cameleer/cameleer-runtime-base:latest}
labels:
- traefik.enable=true
- traefik.http.routers.saas.rule=PathPrefix(`/platform`)

View File

@@ -14,7 +14,7 @@ services:
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}
image: ${SERVER_IMAGE:-registry.cameleer.io/cameleer/cameleer-server}:${VERSION:-latest}
container_name: cameleer-server
restart: unless-stopped
depends_on:
@@ -40,7 +40,7 @@ services:
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}
CAMELEER_SERVER_RUNTIME_BASEIMAGE: registry.cameleer.io/cameleer/cameleer-runtime-base:${VERSION:-latest}
labels:
- traefik.enable=true
- traefik.http.routers.server-api.rule=PathPrefix(`/api`)
@@ -67,7 +67,7 @@ services:
- monitoring
cameleer-server-ui:
image: ${SERVER_UI_IMAGE:-gitea.siegeln.net/cameleer/cameleer-server-ui}:${VERSION:-latest}
image: ${SERVER_UI_IMAGE:-registry.cameleer.io/cameleer/cameleer-server-ui}:${VERSION:-latest}
restart: unless-stopped
depends_on:
cameleer-server:

View File

@@ -3,7 +3,7 @@
services:
cameleer-traefik:
image: ${TRAEFIK_IMAGE:-gitea.siegeln.net/cameleer/cameleer-traefik}:${VERSION:-latest}
image: ${TRAEFIK_IMAGE:-registry.cameleer.io/cameleer/cameleer-traefik}:${VERSION:-latest}
restart: unless-stopped
ports:
- "${HTTP_PORT:-80}:80"
@@ -28,7 +28,7 @@ services:
- monitoring
cameleer-postgres:
image: ${POSTGRES_IMAGE:-gitea.siegeln.net/cameleer/cameleer-postgres}:${VERSION:-latest}
image: ${POSTGRES_IMAGE:-registry.cameleer.io/cameleer/cameleer-postgres}:${VERSION:-latest}
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB:-cameleer_saas}
@@ -46,7 +46,7 @@ services:
- monitoring
cameleer-clickhouse:
image: ${CLICKHOUSE_IMAGE:-gitea.siegeln.net/cameleer/cameleer-clickhouse}:${VERSION:-latest}
image: ${CLICKHOUSE_IMAGE:-registry.cameleer.io/cameleer/cameleer-clickhouse}:${VERSION:-latest}
restart: unless-stopped
environment:
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:?CLICKHOUSE_PASSWORD must be set in .env}