Harden install pipeline: health checks, log rotation, backup timer

- install.sh: Add Docker daemon check, 10GB disk space pre-flight,
  error handling on pull/up, post-startup health polling with crash
  detection, cleanup trap on failure
- docker-compose: Fix nginx/listmonk depends_on to service_healthy,
  add x-logging anchor (10m/3 files) to all ~39 services
- config.sh: Preserve existing secrets on re-run (reconfigure mode),
  add automated daily backup timer (systemd, 02:00, 30-day retention)
- mirror-images.sh: Fix gotify source tag (2.9.0 not v2.9.0)
- build-release.sh: Ensure mkdocs/docs and mkdocs/overrides dirs exist
- .env.example: Add COMPOSE_PROFILES variable

Bunker Admin
This commit is contained in:
bunker-admin 2026-03-25 19:33:11 -06:00
parent 3262d92065
commit 7287328148
9 changed files with 556 additions and 67 deletions

View File

@ -179,6 +179,10 @@ VIDEO_PREVIEW_LINK_EXPIRY_HOURS=24
# Leave IMAGE_TAG blank/unset (defaults to 'local') to build locally from source. # Leave IMAGE_TAG blank/unset (defaults to 'local') to build locally from source.
GITEA_REGISTRY=gitea.bnkops.com/admin GITEA_REGISTRY=gitea.bnkops.com/admin
IMAGE_TAG= IMAGE_TAG=
# Docker Compose profiles — set to 'monitoring' to include Prometheus/Grafana/Alertmanager
# in every 'docker compose up -d'. Leave blank to start monitoring separately.
COMPOSE_PROFILES=
# Credentials used by the registry status API endpoint (GET /api/registry/status) # Credentials used by the registry status API endpoint (GET /api/registry/status)
# For docker push/pull, run: docker login gitea.bnkops.com # For docker push/pull, run: docker login gitea.bnkops.com
GITEA_REGISTRY_USER=admin GITEA_REGISTRY_USER=admin

262
config.sh
View File

@ -180,6 +180,8 @@ initialize_env() {
exit 1 exit 1
fi fi
RECONFIGURE_MODE=false
if [[ -f "$ENV_FILE" ]]; then if [[ -f "$ENV_FILE" ]]; then
warn "Existing .env file found at $ENV_FILE" warn "Existing .env file found at $ENV_FILE"
if prompt_yes_no "Back up existing .env and create a fresh one?"; then if prompt_yes_no "Back up existing .env and create a fresh one?"; then
@ -187,7 +189,8 @@ initialize_env() {
cp "$ENV_EXAMPLE" "$ENV_FILE" cp "$ENV_EXAMPLE" "$ENV_FILE"
success "Created fresh .env from .env.example" success "Created fresh .env from .env.example"
else else
info "Keeping existing .env. Will update values in place." info "Keeping existing .env. Existing secrets will be preserved."
RECONFIGURE_MODE=true
fi fi
else else
cp "$ENV_EXAMPLE" "$ENV_FILE" cp "$ENV_EXAMPLE" "$ENV_FILE"
@ -283,12 +286,37 @@ configure_admin() {
success "Admin credentials configured" success "Admin credentials configured"
} }
# Check if a key already has a real value in .env (non-empty, not a placeholder)
env_var_is_set() {
local key=$1
local val
val=$(grep "^${key}=" "$ENV_FILE" 2>/dev/null | head -1 | cut -d= -f2-)
[[ -n "$val" && "$val" != "changeme" && "$val" != *"example"* && "$val" != *"CHANGEME"* ]]
}
# Update an env var only if not already set (for reconfigure mode)
update_env_var_if_empty() {
local key=$1
local value=$2
if [[ "$RECONFIGURE_MODE" == "true" ]] && env_var_is_set "$key"; then
return 1 # signal: kept existing
fi
update_env_var "$key" "$value"
return 0
}
generate_all_secrets() { generate_all_secrets() {
header "Generating Secrets" header "Generating Secrets"
info "Auto-generating 22 unique secrets and passwords..." if [[ "$RECONFIGURE_MODE" == "true" ]]; then
info "Reconfigure mode: existing secrets will be preserved."
else
info "Auto-generating 22 unique secrets and passwords..."
fi
echo "" echo ""
local generated=0 kept=0
# JWT & Encryption (64-char hex) # JWT & Encryption (64-char hex)
local jwt_access jwt_refresh jwt_invite enc_key local jwt_access jwt_refresh jwt_invite enc_key
jwt_access=$(generate_secret) jwt_access=$(generate_secret)
@ -296,22 +324,41 @@ generate_all_secrets() {
jwt_invite=$(generate_secret) jwt_invite=$(generate_secret)
enc_key=$(generate_secret) enc_key=$(generate_secret)
update_env_var "JWT_ACCESS_SECRET" "$jwt_access" local jwt_changed=false
update_env_var "JWT_REFRESH_SECRET" "$jwt_refresh" update_env_var_if_empty "JWT_ACCESS_SECRET" "$jwt_access" && jwt_changed=true
update_env_var "JWT_INVITE_SECRET" "$jwt_invite" update_env_var_if_empty "JWT_REFRESH_SECRET" "$jwt_refresh" && jwt_changed=true
update_env_var "ENCRYPTION_KEY" "$enc_key" update_env_var_if_empty "JWT_INVITE_SECRET" "$jwt_invite" && jwt_changed=true
success "JWT secrets + encryption key" update_env_var_if_empty "ENCRYPTION_KEY" "$enc_key" && jwt_changed=true
if [[ "$jwt_changed" == "true" ]]; then
success "JWT secrets + encryption key"
((generated+=4))
else
info "JWT secrets + encryption key (kept existing)"
((kept+=4))
fi
# Database passwords (24-char alphanum) # Database passwords (24-char alphanum)
local pg_pass redis_pass local pg_pass redis_pass
pg_pass=$(generate_password 24) pg_pass=$(generate_password 24)
redis_pass=$(generate_password 24) redis_pass=$(generate_password 24)
update_env_var "V2_POSTGRES_PASSWORD" "$pg_pass" local db_changed=false
update_env_var "DATABASE_URL" "postgresql://changemaker:${pg_pass}@localhost:5433/changemaker_v2" if update_env_var_if_empty "V2_POSTGRES_PASSWORD" "$pg_pass"; then
update_env_var "REDIS_PASSWORD" "$redis_pass" update_env_var "DATABASE_URL" "postgresql://changemaker:${pg_pass}@localhost:5433/changemaker_v2"
update_env_var "REDIS_URL" "redis://:${redis_pass}@redis-changemaker:6379" db_changed=true
success "PostgreSQL + Redis passwords" fi
update_env_var_if_empty "REDIS_PASSWORD" "$redis_pass" && db_changed=true
if [[ "$db_changed" == "true" ]]; then
# Rebuild REDIS_URL if password changed
local current_redis_pass
current_redis_pass=$(grep "^REDIS_PASSWORD=" "$ENV_FILE" | cut -d= -f2-)
update_env_var "REDIS_URL" "redis://:${current_redis_pass}@redis-changemaker:6379"
success "PostgreSQL + Redis passwords"
((generated+=2))
else
info "PostgreSQL + Redis passwords (kept existing)"
((kept+=2))
fi
# Listmonk # Listmonk
local lm_db_pass lm_web_pass lm_api_token local lm_db_pass lm_web_pass lm_api_token
@ -319,72 +366,133 @@ generate_all_secrets() {
lm_web_pass=$(generate_password 20) lm_web_pass=$(generate_password 20)
lm_api_token=$(openssl rand -hex 16) lm_api_token=$(openssl rand -hex 16)
update_env_var "LISTMONK_DB_PASSWORD" "$lm_db_pass" local lm_changed=false
update_env_var "LISTMONK_WEB_ADMIN_PASSWORD" "$lm_web_pass" update_env_var_if_empty "LISTMONK_DB_PASSWORD" "$lm_db_pass" && lm_changed=true
update_env_var "LISTMONK_API_TOKEN" "$lm_api_token" update_env_var_if_empty "LISTMONK_WEB_ADMIN_PASSWORD" "$lm_web_pass" && lm_changed=true
update_env_var "LISTMONK_ADMIN_PASSWORD" "$lm_api_token" if update_env_var_if_empty "LISTMONK_API_TOKEN" "$lm_api_token"; then
success "Listmonk passwords + API token" update_env_var "LISTMONK_ADMIN_PASSWORD" "$lm_api_token"
lm_changed=true
fi
if [[ "$lm_changed" == "true" ]]; then
success "Listmonk passwords + API token"
((generated+=3))
else
info "Listmonk passwords + API token (kept existing)"
((kept+=3))
fi
# NocoDB # NocoDB
local nc_pass local nc_pass
nc_pass=$(generate_password 20) nc_pass=$(generate_password 20)
update_env_var "NC_ADMIN_PASSWORD" "$nc_pass" if update_env_var_if_empty "NC_ADMIN_PASSWORD" "$nc_pass"; then
success "NocoDB admin password" success "NocoDB admin password"
((generated++))
else
info "NocoDB admin password (kept existing)"
((kept++))
fi
# Gitea # Gitea
local gitea_db gitea_root local gitea_db gitea_root
gitea_db=$(generate_password 20) gitea_db=$(generate_password 20)
gitea_root=$(generate_password 20) gitea_root=$(generate_password 20)
update_env_var "GITEA_DB_PASSWD" "$gitea_db" local gitea_changed=false
update_env_var "GITEA_DB_ROOT_PASSWORD" "$gitea_root" update_env_var_if_empty "GITEA_DB_PASSWD" "$gitea_db" && gitea_changed=true
success "Gitea database passwords" update_env_var_if_empty "GITEA_DB_ROOT_PASSWORD" "$gitea_root" && gitea_changed=true
if [[ "$gitea_changed" == "true" ]]; then
success "Gitea database passwords"
((generated+=2))
else
info "Gitea database passwords (kept existing)"
((kept+=2))
fi
# n8n # n8n
local n8n_enc n8n_pass local n8n_enc n8n_pass
n8n_enc=$(generate_password 32) n8n_enc=$(generate_password 32)
n8n_pass=$(generate_password 20) n8n_pass=$(generate_password 20)
update_env_var "N8N_ENCRYPTION_KEY" "$n8n_enc" local n8n_changed=false
update_env_var "N8N_USER_PASSWORD" "$n8n_pass" update_env_var_if_empty "N8N_ENCRYPTION_KEY" "$n8n_enc" && n8n_changed=true
success "n8n encryption key + admin password" update_env_var_if_empty "N8N_USER_PASSWORD" "$n8n_pass" && n8n_changed=true
if [[ "$n8n_changed" == "true" ]]; then
success "n8n encryption key + admin password"
((generated+=2))
else
info "n8n encryption key + admin password (kept existing)"
((kept+=2))
fi
# Monitoring # Monitoring
local grafana_pass gotify_pass local grafana_pass gotify_pass
grafana_pass=$(generate_password 20) grafana_pass=$(generate_password 20)
gotify_pass=$(generate_password 20) gotify_pass=$(generate_password 20)
update_env_var "GRAFANA_ADMIN_PASSWORD" "$grafana_pass" local mon_changed=false
update_env_var "GOTIFY_ADMIN_PASSWORD" "$gotify_pass" update_env_var_if_empty "GRAFANA_ADMIN_PASSWORD" "$grafana_pass" && mon_changed=true
success "Grafana + Gotify admin passwords" update_env_var_if_empty "GOTIFY_ADMIN_PASSWORD" "$gotify_pass" && mon_changed=true
if [[ "$mon_changed" == "true" ]]; then
success "Grafana + Gotify admin passwords"
((generated+=2))
else
info "Grafana + Gotify admin passwords (kept existing)"
((kept+=2))
fi
# Vaultwarden # Vaultwarden
local vw_admin_token local vw_admin_token
vw_admin_token=$(generate_secret) vw_admin_token=$(generate_secret)
update_env_var "VAULTWARDEN_ADMIN_TOKEN" "$vw_admin_token" if update_env_var_if_empty "VAULTWARDEN_ADMIN_TOKEN" "$vw_admin_token"; then
success "Vaultwarden admin token" success "Vaultwarden admin token"
((generated++))
else
info "Vaultwarden admin token (kept existing)"
((kept++))
fi
# Rocket.Chat # Rocket.Chat
local rc_pass local rc_pass
rc_pass=$(generate_password 20) rc_pass=$(generate_password 20)
update_env_var "ROCKETCHAT_ADMIN_PASSWORD" "$rc_pass" if update_env_var_if_empty "ROCKETCHAT_ADMIN_PASSWORD" "$rc_pass"; then
success "Rocket.Chat admin password" success "Rocket.Chat admin password"
((generated++))
else
info "Rocket.Chat admin password (kept existing)"
((kept++))
fi
# Gancio # Gancio
local gancio_pass local gancio_pass
gancio_pass=$(generate_password 20) gancio_pass=$(generate_password 20)
update_env_var "GANCIO_ADMIN_PASSWORD" "$gancio_pass" if update_env_var_if_empty "GANCIO_ADMIN_PASSWORD" "$gancio_pass"; then
success "Gancio admin password" success "Gancio admin password"
((generated++))
else
info "Gancio admin password (kept existing)"
((kept++))
fi
# Jitsi Meet # Jitsi Meet
local jitsi_secret jitsi_jicofo jitsi_jvb local jitsi_secret jitsi_jicofo jitsi_jvb
jitsi_secret=$(generate_secret) jitsi_secret=$(generate_secret)
jitsi_jicofo=$(openssl rand -hex 16) jitsi_jicofo=$(openssl rand -hex 16)
jitsi_jvb=$(openssl rand -hex 16) jitsi_jvb=$(openssl rand -hex 16)
update_env_var "JITSI_APP_SECRET" "$jitsi_secret" local jitsi_changed=false
update_env_var "JITSI_JICOFO_AUTH_PASSWORD" "$jitsi_jicofo" update_env_var_if_empty "JITSI_APP_SECRET" "$jitsi_secret" && jitsi_changed=true
update_env_var "JITSI_JVB_AUTH_PASSWORD" "$jitsi_jvb" update_env_var_if_empty "JITSI_JICOFO_AUTH_PASSWORD" "$jitsi_jicofo" && jitsi_changed=true
success "Jitsi Meet secrets (JWT + XMPP)" update_env_var_if_empty "JITSI_JVB_AUTH_PASSWORD" "$jitsi_jvb" && jitsi_changed=true
if [[ "$jitsi_changed" == "true" ]]; then
success "Jitsi Meet secrets (JWT + XMPP)"
((generated+=3))
else
info "Jitsi Meet secrets (kept existing)"
((kept+=3))
fi
echo "" echo ""
success "All 21 secrets generated. No placeholder passwords remain." if [[ $kept -gt 0 ]]; then
success "Secrets: ${generated} generated, ${kept} preserved from existing .env"
else
success "All 22 secrets generated. No placeholder passwords remain."
fi
} }
configure_smtp() { configure_smtp() {
@ -519,6 +627,14 @@ configure_features() {
DOCS_COMMENTS_ENABLED="no" DOCS_COMMENTS_ENABLED="no"
fi fi
if prompt_yes_no "Enable Monitoring stack (Prometheus, Grafana, Alertmanager, cAdvisor)?" "y"; then
update_env_var "COMPOSE_PROFILES" "monitoring"
success "Monitoring enabled (COMPOSE_PROFILES=monitoring)"
MONITORING_ENABLED="yes"
else
MONITORING_ENABLED="no"
fi
if prompt_yes_no "Enable Bunker Ops (fleet metrics push to central server)?"; then if prompt_yes_no "Enable Bunker Ops (fleet metrics push to central server)?"; then
update_env_var "BUNKER_OPS_ENABLED" "true" update_env_var "BUNKER_OPS_ENABLED" "true"
success "Bunker Ops enabled" success "Bunker Ops enabled"
@ -994,16 +1110,21 @@ fix_container_permissions() {
local -a dirs=( local -a dirs=(
"configs/code-server/.config:Code Server config" "configs/code-server/.config:Code Server config"
"configs/code-server/.local:Code Server local data" "configs/code-server/.local:Code Server local data"
"mkdocs:MkDocs root"
"mkdocs/docs:MkDocs source docs"
"mkdocs/overrides:MkDocs template overrides"
"mkdocs/.cache:MkDocs cache" "mkdocs/.cache:MkDocs cache"
"mkdocs/site:MkDocs built site" "mkdocs/site:MkDocs built site"
"assets/uploads:Listmonk uploads" "assets/uploads:Listmonk uploads"
"assets/images:Shared images" "assets/images:Shared images"
"assets/icons:Homepage icons" "assets/icons:Homepage icons"
"media/local/inbox:Media upload inbox" "media/local/inbox:Media upload inbox"
"media/local/photos:Media photos"
"media/local/thumbnails:Video thumbnails" "media/local/thumbnails:Video thumbnails"
"media/public:Public media files" "media/public:Public media files"
"local-files:n8n local files" "local-files:n8n local files"
"data:NAR import data" "data:NAR import data"
"data/upgrade:Upgrade trigger directory"
) )
local errors=0 local errors=0
@ -1085,6 +1206,59 @@ install_upgrade_watcher() {
fi fi
} }
# =============================================================================
# Automated Backups (systemd)
# =============================================================================
install_backup_timer() {
header "Automated Backups"
info "Daily automated backups protect against data loss."
info "Backs up PostgreSQL databases + uploads to ./backups/ with 30-day retention."
echo ""
local unit_src="$SCRIPT_DIR/scripts/systemd"
if [[ ! -f "$unit_src/changemaker-backup.timer" ]] || [[ ! -f "$unit_src/changemaker-backup.service" ]]; then
warn "Systemd backup unit templates not found in scripts/systemd/ — skipping"
BACKUP_TIMER="skipped"
return
fi
if ! command -v systemctl &>/dev/null; then
warn "systemctl not found — skipping (not a systemd host?)"
BACKUP_TIMER="skipped"
return
fi
if prompt_yes_no "Install daily automated backups (requires sudo)?" "y"; then
local tmp_timer tmp_service
tmp_timer=$(mktemp)
tmp_service=$(mktemp)
sed -e "s|__PROJECT_DIR__|$SCRIPT_DIR|g" "$unit_src/changemaker-backup.timer" > "$tmp_timer"
sed -e "s|__PROJECT_DIR__|$SCRIPT_DIR|g" -e "s|__USER__|$(whoami)|g" "$unit_src/changemaker-backup.service" > "$tmp_service"
if sudo cp "$tmp_timer" /etc/systemd/system/changemaker-backup.timer \
&& sudo cp "$tmp_service" /etc/systemd/system/changemaker-backup.service \
&& sudo systemctl daemon-reload \
&& sudo systemctl enable --now changemaker-backup.timer; then
success "Daily backup timer installed and enabled (runs at 02:00)"
BACKUP_TIMER="yes"
else
warn "Failed to install systemd units (sudo may have failed)"
warn "Install manually later:"
echo -e " ${CYAN}sudo cp scripts/systemd/changemaker-backup.* /etc/systemd/system/${NC}"
echo -e " ${CYAN}sudo systemctl daemon-reload && sudo systemctl enable --now changemaker-backup.timer${NC}"
BACKUP_TIMER="manual"
fi
rm -f "$tmp_timer" "$tmp_service"
else
info "Skipped. Run backups manually: ./scripts/backup.sh"
BACKUP_TIMER="skipped"
fi
}
# ============================================================================= # =============================================================================
# Summary & Next Steps # Summary & Next Steps
# ============================================================================= # =============================================================================
@ -1103,10 +1277,12 @@ print_summary() {
echo -e " ${BOLD}Gancio sync:${NC} ${GANCIO_SYNC:-no}" echo -e " ${BOLD}Gancio sync:${NC} ${GANCIO_SYNC:-no}"
echo -e " ${BOLD}Jitsi Meet:${NC} ${MEET_ENABLED:-no}" echo -e " ${BOLD}Jitsi Meet:${NC} ${MEET_ENABLED:-no}"
echo -e " ${BOLD}SMS Campaigns:${NC} ${SMS_ENABLED:-no}" echo -e " ${BOLD}SMS Campaigns:${NC} ${SMS_ENABLED:-no}"
echo -e " ${BOLD}Monitoring:${NC} ${MONITORING_ENABLED:-no}"
echo -e " ${BOLD}Docs Comments:${NC} ${DOCS_COMMENTS_ENABLED:-no}" echo -e " ${BOLD}Docs Comments:${NC} ${DOCS_COMMENTS_ENABLED:-no}"
echo -e " ${BOLD}Bunker Ops:${NC} ${BUNKER_OPS_ENABLED:-no}" echo -e " ${BOLD}Bunker Ops:${NC} ${BUNKER_OPS_ENABLED:-no}"
echo -e " ${BOLD}Pangolin:${NC} ${PANGOLIN_CONFIGURED:-no}" echo -e " ${BOLD}Pangolin:${NC} ${PANGOLIN_CONFIGURED:-no}"
echo -e " ${BOLD}Upgrade watcher:${NC} ${UPGRADE_WATCHER:-skipped}" echo -e " ${BOLD}Upgrade watcher:${NC} ${UPGRADE_WATCHER:-skipped}"
echo -e " ${BOLD}Backup timer:${NC} ${BACKUP_TIMER:-skipped}"
echo -e " ${BOLD}Secrets:${NC} 22 auto-generated" echo -e " ${BOLD}Secrets:${NC} 22 auto-generated"
echo "" echo ""
echo -e " ${DIM}Config file: $ENV_FILE${NC}" echo -e " ${DIM}Config file: $ENV_FILE${NC}"
@ -1155,9 +1331,8 @@ print_next_steps() {
echo -e " ${CYAN}docker compose up -d rocketchat${NC} # Team chat" echo -e " ${CYAN}docker compose up -d rocketchat${NC} # Team chat"
echo -e " ${CYAN}docker compose up -d jitsi-web jitsi-prosody jitsi-jicofo jitsi-jvb${NC} # Video calls" echo -e " ${CYAN}docker compose up -d jitsi-web jitsi-prosody jitsi-jicofo jitsi-jvb${NC} # Video calls"
echo -e " ${CYAN}docker compose up -d homepage${NC} # Service dashboard" echo -e " ${CYAN}docker compose up -d homepage${NC} # Service dashboard"
echo -e " ${CYAN}docker compose --profile monitoring up -d${NC} # Monitoring"
echo "" echo ""
echo -e " ${BOLD}5.${NC} Or start everything at once:" echo -e " ${BOLD}5.${NC} Or start everything at once (monitoring included if enabled above):"
echo -e " ${CYAN}docker compose up -d${NC}" echo -e " ${CYAN}docker compose up -d${NC}"
echo "" echo ""
fi fi
@ -1187,12 +1362,17 @@ main() {
generate_services_yaml generate_services_yaml
fix_container_permissions fix_container_permissions
install_upgrade_watcher install_upgrade_watcher
install_backup_timer
# Release mode: auto-set production defaults # Release mode: auto-set production defaults
if [[ "$INSTALL_MODE" == "release" ]]; then if [[ "$INSTALL_MODE" == "release" ]]; then
header "Release Mode Settings" header "Release Mode Settings"
update_env_var "IMAGE_TAG" "latest" update_env_var "IMAGE_TAG" "latest"
update_env_var "NODE_ENV" "production" update_env_var "NODE_ENV" "production"
# Ensure monitoring is included if user opted in
if [[ "${MONITORING_ENABLED:-no}" == "yes" ]]; then
update_env_var "COMPOSE_PROFILES" "monitoring"
fi
success "Set IMAGE_TAG=latest, NODE_ENV=production (pre-built images)" success "Set IMAGE_TAG=latest, NODE_ENV=production (pre-built images)"
fi fi

View File

@ -6,6 +6,12 @@
############################################################################### ###############################################################################
############################################################################### ###############################################################################
x-logging: &default-logging
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
services: services:
# ========================================================================= # =========================================================================
# V2 CORE SERVICES # V2 CORE SERVICES
@ -129,6 +135,7 @@ services:
condition: service_healthy condition: service_healthy
redis: redis:
condition: service_healthy condition: service_healthy
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -176,6 +183,7 @@ services:
depends_on: depends_on:
v2-postgres: v2-postgres:
condition: service_healthy condition: service_healthy
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -202,6 +210,7 @@ services:
- VITE_MKDOCS_SITE_PORT=${MKDOCS_SITE_SERVER_PORT:-4004} - VITE_MKDOCS_SITE_PORT=${MKDOCS_SITE_SERVER_PORT:-4004}
depends_on: depends_on:
- api - api
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -225,6 +234,7 @@ services:
interval: 10s interval: 10s
timeout: 5s timeout: 5s
retries: 5 retries: 5
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -277,8 +287,11 @@ services:
- ./public-web:/usr/share/nginx/public-web:ro - ./public-web:/usr/share/nginx/public-web:ro
- ./configs/pangolin:/etc/pangolin:ro - ./configs/pangolin:/etc/pangolin:ro
depends_on: depends_on:
- api api:
- admin condition: service_healthy
admin:
condition: service_healthy
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -305,6 +318,7 @@ services:
depends_on: depends_on:
v2-postgres: v2-postgres:
condition: service_healthy condition: service_healthy
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -330,6 +344,7 @@ services:
volumes: volumes:
- ./scripts/nocodb-init.sh:/init.sh:ro - ./scripts/nocodb-init.sh:/init.sh:ro
entrypoint: ["/bin/sh", "/init.sh"] entrypoint: ["/bin/sh", "/init.sh"]
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -382,7 +397,8 @@ services:
retries: 3 retries: 3
start_period: 30s start_period: 30s
depends_on: depends_on:
- listmonk-db listmonk-db:
condition: service_healthy
command: [sh, -c, "./listmonk --install --idempotent --yes --config '' && ./listmonk --upgrade --yes --config '' && ./listmonk --config ''"] command: [sh, -c, "./listmonk --install --idempotent --yes --config '' && ./listmonk --upgrade --yes --config '' && ./listmonk --config ''"]
environment: environment:
LISTMONK_app__address: 0.0.0.0:9000 LISTMONK_app__address: 0.0.0.0:9000
@ -397,6 +413,7 @@ services:
LISTMONK_ADMIN_PASSWORD: ${LISTMONK_WEB_ADMIN_PASSWORD:-} LISTMONK_ADMIN_PASSWORD: ${LISTMONK_WEB_ADMIN_PASSWORD:-}
volumes: volumes:
- ./assets/uploads:/listmonk/uploads:rw - ./assets/uploads:/listmonk/uploads:rw
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -417,6 +434,7 @@ services:
retries: 6 retries: 6
volumes: volumes:
- listmonk-data:/var/lib/postgresql/data - listmonk-data:/var/lib/postgresql/data
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -485,6 +503,7 @@ services:
fi fi
echo "[listmonk-init] Done" echo "[listmonk-init] Done"
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -512,6 +531,7 @@ services:
ports: ports:
- "127.0.0.1:${CODE_SERVER_PORT:-8888}:8080" - "127.0.0.1:${CODE_SERVER_PORT:-8888}:8080"
restart: unless-stopped restart: unless-stopped
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -540,6 +560,7 @@ services:
- GANCIO_PORT=${GANCIO_PORT:-8092} - GANCIO_PORT=${GANCIO_PORT:-8092}
entrypoint: ["/bin/sh", "/scripts/mkdocs-entrypoint.sh"] entrypoint: ["/bin/sh", "/scripts/mkdocs-entrypoint.sh"]
restart: unless-stopped restart: unless-stopped
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -557,6 +578,7 @@ services:
ports: ports:
- "127.0.0.1:${MKDOCS_SITE_SERVER_PORT:-4004}:80" - "127.0.0.1:${MKDOCS_SITE_SERVER_PORT:-4004}:80"
restart: unless-stopped restart: unless-stopped
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -587,6 +609,7 @@ services:
volumes: volumes:
- n8n-data:/home/node/.n8n - n8n-data:/home/node/.n8n
- ./local-files:/files - ./local-files:/files
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -608,6 +631,7 @@ services:
- HOMEPAGE_ALLOWED_HOSTS=* - HOMEPAGE_ALLOWED_HOSTS=*
- HOMEPAGE_VAR_BASE_URL=${HOMEPAGE_VAR_BASE_URL:-http://localhost} - HOMEPAGE_VAR_BASE_URL=${HOMEPAGE_VAR_BASE_URL:-http://localhost}
restart: unless-stopped restart: unless-stopped
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -649,6 +673,7 @@ services:
- "127.0.0.1:${GITEA_SSH_PORT:-2222}:22" - "127.0.0.1:${GITEA_SSH_PORT:-2222}:22"
depends_on: depends_on:
- gitea-db - gitea-db
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -668,6 +693,7 @@ services:
interval: 10s interval: 10s
timeout: 5s timeout: 5s
retries: 5 retries: 5
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -678,6 +704,7 @@ services:
ports: ports:
- "127.0.0.1:${MINI_QR_PORT:-8089}:8080" - "127.0.0.1:${MINI_QR_PORT:-8089}:8080"
restart: unless-stopped restart: unless-stopped
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -696,6 +723,7 @@ services:
start_period: 20s start_period: 20s
environment: environment:
- VITE_APP_COLLAB_SERVER_URL=${EXCALIDRAW_WS_URL:-wss://draw.cmlite.org} - VITE_APP_COLLAB_SERVER_URL=${EXCALIDRAW_WS_URL:-wss://draw.cmlite.org}
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -728,6 +756,7 @@ services:
- SMTP_PASSWORD=${SMTP_PASS:-} - SMTP_PASSWORD=${SMTP_PASS:-}
volumes: volumes:
- vaultwarden-data:/data - vaultwarden-data:/data
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -794,6 +823,7 @@ services:
rm -f "$$SESSION_COOKIE" rm -f "$$SESSION_COOKIE"
echo "[vaultwarden-init] Done" echo "[vaultwarden-init] Done"
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -834,6 +864,7 @@ services:
- OVERWRITE_SETTING_VideoConf_Default_Provider=jitsi - OVERWRITE_SETTING_VideoConf_Default_Provider=jitsi
volumes: volumes:
- rocketchat-uploads:/app/uploads - rocketchat-uploads:/app/uploads
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
healthcheck: healthcheck:
@ -849,6 +880,7 @@ services:
container_name: nats-rocketchat container_name: nats-rocketchat
restart: unless-stopped restart: unless-stopped
command: --http_port 8222 command: --http_port 8222
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -860,6 +892,7 @@ services:
command: ["mongod", "--replSet", "rs0", "--bind_ip_all"] command: ["mongod", "--replSet", "rs0", "--bind_ip_all"]
volumes: volumes:
- mongodb-rocketchat-data:/data/db - mongodb-rocketchat-data:/data/db
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
healthcheck: healthcheck:
@ -897,6 +930,7 @@ services:
- server__baseurl=${GANCIO_BASE_URL:-https://events.cmlite.org} - server__baseurl=${GANCIO_BASE_URL:-https://events.cmlite.org}
volumes: volumes:
- gancio-data:/home/node/data - gancio-data:/home/node/data
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -938,6 +972,7 @@ services:
ON CONFLICT (key) DO NOTHING;" ON CONFLICT (key) DO NOTHING;"
echo "Gancio theme settings seeded." echo "Gancio theme settings seeded."
restart: "no" restart: "no"
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -976,6 +1011,7 @@ services:
timeout: 5s timeout: 5s
retries: 5 retries: 5
start_period: 30s start_period: 30s
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -1009,6 +1045,7 @@ services:
timeout: 5s timeout: 5s
retries: 5 retries: 5
start_period: 30s start_period: 30s
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -1032,6 +1069,7 @@ services:
- TZ=UTC - TZ=UTC
volumes: volumes:
- jitsi-jicofo-config:/config - jitsi-jicofo-config:/config
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -1057,6 +1095,7 @@ services:
- TZ=UTC - TZ=UTC
volumes: volumes:
- jitsi-jvb-config:/config - jitsi-jvb-config:/config
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -1091,6 +1130,7 @@ services:
- NEWT_SECRET=${PANGOLIN_NEWT_SECRET} - NEWT_SECRET=${PANGOLIN_NEWT_SECRET}
depends_on: depends_on:
- nginx - nginx
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -1109,6 +1149,7 @@ services:
- SWARM=0 - SWARM=0
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro - /var/run/docker.sock:/var/run/docker.sock:ro
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -1129,6 +1170,7 @@ services:
- ./configs/prometheus:/etc/prometheus - ./configs/prometheus:/etc/prometheus
- prometheus-data:/prometheus - prometheus-data:/prometheus
restart: always restart: always
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
profiles: profiles:
@ -1152,6 +1194,7 @@ services:
restart: always restart: always
depends_on: depends_on:
- prometheus - prometheus
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
profiles: profiles:
@ -1173,6 +1216,7 @@ services:
devices: devices:
- /dev/kmsg - /dev/kmsg
restart: always restart: always
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
profiles: profiles:
@ -1193,6 +1237,7 @@ services:
- /sys:/host/sys:ro - /sys:/host/sys:ro
- /:/rootfs:ro - /:/rootfs:ro
restart: always restart: always
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
profiles: profiles:
@ -1209,6 +1254,7 @@ services:
restart: always restart: always
depends_on: depends_on:
- redis - redis
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
profiles: profiles:
@ -1226,6 +1272,7 @@ services:
- '--config.file=/etc/alertmanager/alertmanager.yml' - '--config.file=/etc/alertmanager/alertmanager.yml'
- '--storage.path=/alertmanager' - '--storage.path=/alertmanager'
restart: always restart: always
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
profiles: profiles:
@ -1243,6 +1290,7 @@ services:
volumes: volumes:
- gotify-data:/app/data - gotify-data:/app/data
restart: always restart: always
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
profiles: profiles:

View File

@ -2,6 +2,12 @@
# Changemaker Lite v2 — Docker Compose # Changemaker Lite v2 — Docker Compose
############################################################################### ###############################################################################
x-logging: &default-logging
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
services: services:
# ========================================================================= # =========================================================================
# V2 CORE SERVICES # V2 CORE SERVICES
@ -130,6 +136,7 @@ services:
condition: service_healthy condition: service_healthy
redis: redis:
condition: service_healthy condition: service_healthy
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -183,6 +190,7 @@ services:
depends_on: depends_on:
v2-postgres: v2-postgres:
condition: service_healthy condition: service_healthy
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -215,6 +223,7 @@ services:
- /app/node_modules - /app/node_modules
depends_on: depends_on:
- api - api
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -238,6 +247,7 @@ services:
interval: 10s interval: 10s
timeout: 5s timeout: 5s
retries: 5 retries: 5
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -292,8 +302,11 @@ services:
- ./public-web:/usr/share/nginx/public-web:ro - ./public-web:/usr/share/nginx/public-web:ro
- ./configs/pangolin:/etc/pangolin:ro - ./configs/pangolin:/etc/pangolin:ro
depends_on: depends_on:
- api api:
- admin condition: service_healthy
admin:
condition: service_healthy
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -320,6 +333,7 @@ services:
depends_on: depends_on:
v2-postgres: v2-postgres:
condition: service_healthy condition: service_healthy
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -345,6 +359,7 @@ services:
volumes: volumes:
- ./scripts/nocodb-init.sh:/init.sh:ro - ./scripts/nocodb-init.sh:/init.sh:ro
entrypoint: ["/bin/sh", "/init.sh"] entrypoint: ["/bin/sh", "/init.sh"]
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -397,7 +412,8 @@ services:
retries: 3 retries: 3
start_period: 30s start_period: 30s
depends_on: depends_on:
- listmonk-db listmonk-db:
condition: service_healthy
command: [sh, -c, "./listmonk --install --idempotent --yes --config '' && ./listmonk --upgrade --yes --config '' && ./listmonk --config ''"] command: [sh, -c, "./listmonk --install --idempotent --yes --config '' && ./listmonk --upgrade --yes --config '' && ./listmonk --config ''"]
environment: environment:
LISTMONK_app__address: 0.0.0.0:9000 LISTMONK_app__address: 0.0.0.0:9000
@ -412,6 +428,7 @@ services:
LISTMONK_ADMIN_PASSWORD: ${LISTMONK_WEB_ADMIN_PASSWORD:-} LISTMONK_ADMIN_PASSWORD: ${LISTMONK_WEB_ADMIN_PASSWORD:-}
volumes: volumes:
- ./assets/uploads:/listmonk/uploads:rw - ./assets/uploads:/listmonk/uploads:rw
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -432,6 +449,7 @@ services:
retries: 6 retries: 6
volumes: volumes:
- listmonk-data:/var/lib/postgresql/data - listmonk-data:/var/lib/postgresql/data
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -500,6 +518,7 @@ services:
fi fi
echo "[listmonk-init] Done" echo "[listmonk-init] Done"
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -532,6 +551,7 @@ services:
ports: ports:
- "127.0.0.1:${CODE_SERVER_PORT:-8888}:8080" - "127.0.0.1:${CODE_SERVER_PORT:-8888}:8080"
restart: unless-stopped restart: unless-stopped
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -560,6 +580,7 @@ services:
- GANCIO_PORT=${GANCIO_PORT:-8092} - GANCIO_PORT=${GANCIO_PORT:-8092}
entrypoint: ["/bin/sh", "/scripts/mkdocs-entrypoint.sh"] entrypoint: ["/bin/sh", "/scripts/mkdocs-entrypoint.sh"]
restart: unless-stopped restart: unless-stopped
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -577,6 +598,7 @@ services:
ports: ports:
- "127.0.0.1:${MKDOCS_SITE_SERVER_PORT:-4004}:80" - "127.0.0.1:${MKDOCS_SITE_SERVER_PORT:-4004}:80"
restart: unless-stopped restart: unless-stopped
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -607,6 +629,7 @@ services:
volumes: volumes:
- n8n-data:/home/node/.n8n - n8n-data:/home/node/.n8n
- ./local-files:/files - ./local-files:/files
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -628,6 +651,7 @@ services:
- HOMEPAGE_ALLOWED_HOSTS=* - HOMEPAGE_ALLOWED_HOSTS=*
- HOMEPAGE_VAR_BASE_URL=${HOMEPAGE_VAR_BASE_URL:-http://localhost} - HOMEPAGE_VAR_BASE_URL=${HOMEPAGE_VAR_BASE_URL:-http://localhost}
restart: unless-stopped restart: unless-stopped
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -669,6 +693,7 @@ services:
- "127.0.0.1:${GITEA_SSH_PORT:-2222}:22" - "127.0.0.1:${GITEA_SSH_PORT:-2222}:22"
depends_on: depends_on:
- gitea-db - gitea-db
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -688,6 +713,7 @@ services:
interval: 10s interval: 10s
timeout: 5s timeout: 5s
retries: 5 retries: 5
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -698,6 +724,7 @@ services:
ports: ports:
- "127.0.0.1:${MINI_QR_PORT:-8089}:8080" - "127.0.0.1:${MINI_QR_PORT:-8089}:8080"
restart: unless-stopped restart: unless-stopped
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -716,6 +743,7 @@ services:
start_period: 20s start_period: 20s
environment: environment:
- VITE_APP_COLLAB_SERVER_URL=${EXCALIDRAW_WS_URL:-wss://draw.cmlite.org} - VITE_APP_COLLAB_SERVER_URL=${EXCALIDRAW_WS_URL:-wss://draw.cmlite.org}
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -748,6 +776,7 @@ services:
- SMTP_PASSWORD=${SMTP_PASS:-} - SMTP_PASSWORD=${SMTP_PASS:-}
volumes: volumes:
- vaultwarden-data:/data - vaultwarden-data:/data
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -814,6 +843,7 @@ services:
rm -f "$$SESSION_COOKIE" rm -f "$$SESSION_COOKIE"
echo "[vaultwarden-init] Done" echo "[vaultwarden-init] Done"
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -854,6 +884,7 @@ services:
- OVERWRITE_SETTING_VideoConf_Default_Provider=jitsi - OVERWRITE_SETTING_VideoConf_Default_Provider=jitsi
volumes: volumes:
- rocketchat-uploads:/app/uploads - rocketchat-uploads:/app/uploads
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
healthcheck: healthcheck:
@ -869,6 +900,7 @@ services:
container_name: nats-rocketchat container_name: nats-rocketchat
restart: unless-stopped restart: unless-stopped
command: --http_port 8222 command: --http_port 8222
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -880,6 +912,7 @@ services:
command: ["mongod", "--replSet", "rs0", "--bind_ip_all"] command: ["mongod", "--replSet", "rs0", "--bind_ip_all"]
volumes: volumes:
- mongodb-rocketchat-data:/data/db - mongodb-rocketchat-data:/data/db
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
healthcheck: healthcheck:
@ -917,6 +950,7 @@ services:
- server__baseurl=${GANCIO_BASE_URL:-https://events.cmlite.org} - server__baseurl=${GANCIO_BASE_URL:-https://events.cmlite.org}
volumes: volumes:
- gancio-data:/home/node/data - gancio-data:/home/node/data
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -958,6 +992,7 @@ services:
ON CONFLICT (key) DO NOTHING;" ON CONFLICT (key) DO NOTHING;"
echo "Gancio theme settings seeded." echo "Gancio theme settings seeded."
restart: "no" restart: "no"
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -996,6 +1031,7 @@ services:
timeout: 5s timeout: 5s
retries: 5 retries: 5
start_period: 30s start_period: 30s
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -1029,6 +1065,7 @@ services:
timeout: 5s timeout: 5s
retries: 5 retries: 5
start_period: 30s start_period: 30s
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -1052,6 +1089,7 @@ services:
- TZ=UTC - TZ=UTC
volumes: volumes:
- jitsi-jicofo-config:/config - jitsi-jicofo-config:/config
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -1077,6 +1115,7 @@ services:
- TZ=UTC - TZ=UTC
volumes: volumes:
- jitsi-jvb-config:/config - jitsi-jvb-config:/config
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -1111,6 +1150,7 @@ services:
- NEWT_SECRET=${PANGOLIN_NEWT_SECRET} - NEWT_SECRET=${PANGOLIN_NEWT_SECRET}
depends_on: depends_on:
- nginx - nginx
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -1129,6 +1169,7 @@ services:
- SWARM=0 - SWARM=0
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro - /var/run/docker.sock:/var/run/docker.sock:ro
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
@ -1149,6 +1190,7 @@ services:
- ./configs/prometheus:/etc/prometheus - ./configs/prometheus:/etc/prometheus
- prometheus-data:/prometheus - prometheus-data:/prometheus
restart: always restart: always
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
profiles: profiles:
@ -1172,6 +1214,7 @@ services:
restart: always restart: always
depends_on: depends_on:
- prometheus - prometheus
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
profiles: profiles:
@ -1193,6 +1236,7 @@ services:
devices: devices:
- /dev/kmsg - /dev/kmsg
restart: always restart: always
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
profiles: profiles:
@ -1213,6 +1257,7 @@ services:
- /sys:/host/sys:ro - /sys:/host/sys:ro
- /:/rootfs:ro - /:/rootfs:ro
restart: always restart: always
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
profiles: profiles:
@ -1229,6 +1274,7 @@ services:
restart: always restart: always
depends_on: depends_on:
- redis - redis
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
profiles: profiles:
@ -1246,6 +1292,7 @@ services:
- '--config.file=/etc/alertmanager/alertmanager.yml' - '--config.file=/etc/alertmanager/alertmanager.yml'
- '--storage.path=/alertmanager' - '--storage.path=/alertmanager'
restart: always restart: always
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
profiles: profiles:
@ -1263,6 +1310,7 @@ services:
volumes: volumes:
- gotify-data:/app/data - gotify-data:/app/data
restart: always restart: always
logging: *default-logging
networks: networks:
- changemaker-lite - changemaker-lite
profiles: profiles:

View File

@ -144,8 +144,8 @@ info "Nginx templates (for reference)"
if [[ -d "$PROJECT_DIR/mkdocs" ]]; then if [[ -d "$PROJECT_DIR/mkdocs" ]]; then
mkdir -p "$STAGE_DIR/mkdocs" mkdir -p "$STAGE_DIR/mkdocs"
cp "$PROJECT_DIR/mkdocs/mkdocs.yml" "$STAGE_DIR/mkdocs/" 2>/dev/null || true cp "$PROJECT_DIR/mkdocs/mkdocs.yml" "$STAGE_DIR/mkdocs/" 2>/dev/null || true
cp -r "$PROJECT_DIR/mkdocs/docs" "$STAGE_DIR/mkdocs/" 2>/dev/null || true cp -r "$PROJECT_DIR/mkdocs/docs" "$STAGE_DIR/mkdocs/" 2>/dev/null || mkdir -p "$STAGE_DIR/mkdocs/docs"
cp -r "$PROJECT_DIR/mkdocs/overrides" "$STAGE_DIR/mkdocs/" 2>/dev/null || true cp -r "$PROJECT_DIR/mkdocs/overrides" "$STAGE_DIR/mkdocs/" 2>/dev/null || mkdir -p "$STAGE_DIR/mkdocs/overrides"
mkdir -p "$STAGE_DIR/mkdocs/.cache" mkdir -p "$STAGE_DIR/mkdocs/.cache"
mkdir -p "$STAGE_DIR/mkdocs/site" mkdir -p "$STAGE_DIR/mkdocs/site"
info "MkDocs (starter documentation)" info "MkDocs (starter documentation)"

View File

@ -21,6 +21,14 @@ REPO="admin/changemaker.lite"
INSTALL_DIR="${HOME}/changemaker.lite" INSTALL_DIR="${HOME}/changemaker.lite"
VERSION="" VERSION=""
LOCAL_TARBALL="" LOCAL_TARBALL=""
MIN_DISK_MB=10000
HEALTH_TIMEOUT=180
HEALTH_INTERVAL=5
# --- State flags for cleanup ---
EXTRACT_DIR=""
TARBALL_PATH=""
CONFIG_COMPLETE=false
# --- Colors --- # --- Colors ---
if [[ -t 1 ]] && [[ -z "${NO_COLOR:-}" ]]; then if [[ -t 1 ]] && [[ -z "${NO_COLOR:-}" ]]; then
@ -35,6 +43,28 @@ success() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; } error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
# --- Cleanup on failure ---
cleanup() {
local exit_code=$?
if [[ $exit_code -ne 0 ]]; then
# Clean up temp extraction directory
if [[ -n "$EXTRACT_DIR" ]] && [[ -d "$EXTRACT_DIR" ]]; then
rm -rf "$EXTRACT_DIR"
fi
# Clean up downloaded tarball (but not user-provided ones)
if [[ -z "$LOCAL_TARBALL" ]] && [[ -n "$TARBALL_PATH" ]] && [[ -f "$TARBALL_PATH" ]]; then
rm -f "$TARBALL_PATH"
fi
# Remove install dir only if config wizard never ran (no user data to lose)
if [[ "$CONFIG_COMPLETE" == "false" ]] && [[ -d "$INSTALL_DIR" ]] && [[ ! -f "$INSTALL_DIR/.env" ]]; then
rm -rf "$INSTALL_DIR"
fi
echo ""
error "Installation failed. See errors above."
fi
}
trap cleanup EXIT
# --- Arg parser --- # --- Arg parser ---
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case "$1" in case "$1" in
@ -51,7 +81,10 @@ done
echo -e "${BOLD}Changemaker Lite — Installer${NC}" echo -e "${BOLD}Changemaker Lite — Installer${NC}"
echo "" echo ""
# --- Step 1: Check prerequisites --- # =============================================================================
# Step 1: Check prerequisites
# =============================================================================
info "Checking prerequisites..." info "Checking prerequisites..."
MISSING=() MISSING=()
command -v docker >/dev/null 2>&1 || MISSING+=("docker") command -v docker >/dev/null 2>&1 || MISSING+=("docker")
@ -68,7 +101,30 @@ if [[ ${#MISSING[@]} -gt 0 ]]; then
fi fi
success "Prerequisites OK (Docker $(docker --version | grep -oP '\d+\.\d+\.\d+'), OpenSSL available)" success "Prerequisites OK (Docker $(docker --version | grep -oP '\d+\.\d+\.\d+'), OpenSSL available)"
# --- Step 2: Check install directory --- # Docker daemon must be running (not just installed)
if ! docker info >/dev/null 2>&1; then
error "Docker daemon is not running."
echo ""
echo " Start it with: sudo systemctl start docker"
echo " Or: sudo service docker start"
exit 1
fi
success "Docker daemon is running"
# Disk space check
AVAILABLE_MB=$(df -m "$(dirname "$INSTALL_DIR")" | awk 'NR==2 {print $4}')
if [[ "$AVAILABLE_MB" -lt "$MIN_DISK_MB" ]]; then
error "Insufficient disk space: ${AVAILABLE_MB}MB available, ${MIN_DISK_MB}MB required."
echo ""
echo " The full stack (images + volumes) needs ~10GB of free space."
exit 1
fi
success "Disk space: ${AVAILABLE_MB}MB available (${MIN_DISK_MB}MB required)"
# =============================================================================
# Step 2: Check install directory
# =============================================================================
if [[ -d "$INSTALL_DIR" ]]; then if [[ -d "$INSTALL_DIR" ]]; then
if [[ -f "$INSTALL_DIR/docker-compose.yml" ]]; then if [[ -f "$INSTALL_DIR/docker-compose.yml" ]]; then
error "Changemaker Lite is already installed at $INSTALL_DIR" error "Changemaker Lite is already installed at $INSTALL_DIR"
@ -78,8 +134,10 @@ if [[ -d "$INSTALL_DIR" ]]; then
fi fi
fi fi
# --- Step 3: Get tarball --- # =============================================================================
TARBALL_PATH="" # Step 3: Get tarball
# =============================================================================
if [[ -n "$LOCAL_TARBALL" ]]; then if [[ -n "$LOCAL_TARBALL" ]]; then
if [[ ! -f "$LOCAL_TARBALL" ]]; then if [[ ! -f "$LOCAL_TARBALL" ]]; then
error "Tarball not found: $LOCAL_TARBALL" error "Tarball not found: $LOCAL_TARBALL"
@ -101,7 +159,7 @@ else
if [[ -z "$RELEASE_JSON" ]]; then if [[ -z "$RELEASE_JSON" ]]; then
error "Could not fetch release info from ${GITEA_URL}" error "Could not fetch release info from ${GITEA_URL}"
echo "" echo ""
echo "If the registry requires authentication:" echo "If the server is unreachable:"
echo " 1. Download the tarball manually from ${GITEA_URL}/${REPO}/releases" echo " 1. Download the tarball manually from ${GITEA_URL}/${REPO}/releases"
echo " 2. Run: bash install.sh --tarball /path/to/changemaker-lite-*.tar.gz" echo " 2. Run: bash install.sh --tarball /path/to/changemaker-lite-*.tar.gz"
exit 1 exit 1
@ -129,7 +187,10 @@ for a in assets:
success "Downloaded $(du -h "$TARBALL_PATH" | cut -f1)" success "Downloaded $(du -h "$TARBALL_PATH" | cut -f1)"
fi fi
# --- Step 4: Extract --- # =============================================================================
# Step 4: Extract
# =============================================================================
info "Extracting to ${INSTALL_DIR}..." info "Extracting to ${INSTALL_DIR}..."
mkdir -p "$(dirname "$INSTALL_DIR")" mkdir -p "$(dirname "$INSTALL_DIR")"
@ -141,12 +202,12 @@ tar xzf "$TARBALL_PATH" -C "$EXTRACT_DIR"
EXTRACTED=$(find "$EXTRACT_DIR" -maxdepth 1 -mindepth 1 -type d | head -1) EXTRACTED=$(find "$EXTRACT_DIR" -maxdepth 1 -mindepth 1 -type d | head -1)
if [[ -z "$EXTRACTED" ]]; then if [[ -z "$EXTRACTED" ]]; then
error "Tarball extraction failed — no directory found" error "Tarball extraction failed — no directory found"
rm -rf "$EXTRACT_DIR"
exit 1 exit 1
fi fi
mv "$EXTRACTED" "$INSTALL_DIR" mv "$EXTRACTED" "$INSTALL_DIR"
rm -rf "$EXTRACT_DIR" rm -rf "$EXTRACT_DIR"
EXTRACT_DIR="" # Clear so cleanup doesn't try to remove it
# Clean up downloaded tarball # Clean up downloaded tarball
if [[ -z "$LOCAL_TARBALL" ]] && [[ -f "$TARBALL_PATH" ]]; then if [[ -z "$LOCAL_TARBALL" ]] && [[ -f "$TARBALL_PATH" ]]; then
@ -155,23 +216,148 @@ fi
success "Extracted to ${INSTALL_DIR}" success "Extracted to ${INSTALL_DIR}"
# --- Step 5: Run config wizard --- # =============================================================================
# Step 5: Run config wizard
# =============================================================================
echo "" echo ""
echo -e "${BOLD}Starting configuration wizard...${NC}" echo -e "${BOLD}Starting configuration wizard...${NC}"
echo "" echo ""
cd "$INSTALL_DIR" cd "$INSTALL_DIR"
bash config.sh bash config.sh
CONFIG_COMPLETE=true
# =============================================================================
# Step 6: Start services
# =============================================================================
echo ""
echo -e "${BOLD}Configuration complete!${NC}"
echo ""
START_SERVICES="y"
if [[ -t 0 ]]; then
read -rp "Start all services now? [Y/n]: " START_SERVICES
START_SERVICES=${START_SERVICES:-y}
fi
if [[ "$START_SERVICES" =~ ^[Yy]$ ]]; then
echo ""
info "Pulling images from registry (this may take a few minutes on first run)..."
echo ""
cd "$INSTALL_DIR"
if ! docker compose pull 2>&1; then
echo ""
error "Failed to pull images from the registry."
echo ""
echo " Check that the registry is reachable:"
echo " curl -sf https://gitea.bnkops.com/api/v1/repos/admin/changemaker.lite/releases/latest"
echo ""
echo " If pulling from a private registry, log in first:"
echo " docker login gitea.bnkops.com"
echo ""
echo " Then retry:"
echo " cd ${INSTALL_DIR} && docker compose pull && docker compose up -d"
exit 1
fi
success "All images pulled"
echo ""
info "Starting services..."
if ! docker compose up -d 2>&1; then
echo ""
error "Failed to start services."
echo " Check logs: docker compose logs --tail 30"
exit 1
fi
# --- Post-startup health verification ---
echo ""
info "Waiting for services to become healthy (up to ${HEALTH_TIMEOUT}s)..."
info " Database migrations and seeding run automatically on first boot."
echo ""
CORE_SERVICES=("v2-postgres" "redis" "api" "admin")
ELAPSED=0
ALL_HEALTHY=false
while [[ $ELAPSED -lt $HEALTH_TIMEOUT ]]; do
ALL_HEALTHY=true
for svc in "${CORE_SERVICES[@]}"; do
# Detect crashed containers early
state=$(docker compose ps "$svc" --format '{{.State}}' 2>/dev/null || echo "missing")
if [[ "$state" == "exited" || "$state" == "dead" ]]; then
echo ""
error "Service '${svc}' exited unexpectedly. Last logs:"
docker compose logs "$svc" --tail 20 2>/dev/null || true
echo ""
error "Fix the issue and retry: cd ${INSTALL_DIR} && docker compose up -d"
exit 1
fi
# Check health status
health=$(docker compose ps "$svc" --format '{{.Health}}' 2>/dev/null || echo "")
if [[ "$health" != "healthy" ]]; then
ALL_HEALTHY=false
fi
done
if [[ "$ALL_HEALTHY" == "true" ]]; then
break
fi
sleep "$HEALTH_INTERVAL"
ELAPSED=$((ELAPSED + HEALTH_INTERVAL))
done
# Print status table
echo ""
echo -e "${BOLD} Service Status:${NC}"
for svc in "${CORE_SERVICES[@]}"; do
health=$(docker compose ps "$svc" --format '{{.Health}}' 2>/dev/null || echo "unknown")
state=$(docker compose ps "$svc" --format '{{.State}}' 2>/dev/null || echo "unknown")
if [[ "$health" == "healthy" ]]; then
echo -e " ${GREEN}[healthy]${NC} $svc"
elif [[ "$state" == "running" ]]; then
echo -e " ${YELLOW}[starting]${NC} $svc"
else
echo -e " ${RED}[${state}]${NC} $svc"
fi
done
echo ""
if [[ "$ALL_HEALTHY" == "true" ]]; then
success "All core services are healthy! (${ELAPSED}s)"
echo ""
echo " Admin GUI: http://localhost:3000"
echo " API: http://localhost:4000"
echo ""
echo " Check full stack: docker compose ps"
echo " View API logs: docker compose logs -f api --tail 20"
else
warn "Some services are still starting after ${HEALTH_TIMEOUT}s."
echo ""
echo " This may be normal on first boot (migrations + seeding can be slow)."
echo " Monitor progress with:"
echo " docker compose logs -f api --tail 30"
echo ""
echo " Check status with:"
echo " docker compose ps"
fi
else
echo ""
info "Skipped. Start services manually when ready:"
echo ""
echo " cd ${INSTALL_DIR} && docker compose up -d"
echo ""
echo " Pre-built images will be pulled from the registry (~2 min first time)."
echo " Database migrations and seeding run automatically on startup."
fi
# --- Done ---
echo "" echo ""
echo -e "${BOLD}${GREEN}Installation complete!${NC}" echo -e "${BOLD}${GREEN}Installation complete!${NC}"
echo "" echo ""
echo " Start all services:" echo -e " \033[0;33mIMPORTANT: Change your admin password after first login!\033[0m"
echo " cd ${INSTALL_DIR} && docker compose up -d"
echo ""
echo " Check status:"
echo " docker compose ps"
echo ""
echo " View API logs:"
echo " docker compose logs -f api --tail 20"
echo "" echo ""

View File

@ -144,7 +144,7 @@ declare -A MONITORING_IMAGES=(
["oliver006/redis_exporter:v1.81.0"]="redis_exporter:v1.81.0" ["oliver006/redis_exporter:v1.81.0"]="redis_exporter:v1.81.0"
["gcr.io/cadvisor/cadvisor:v0.55.1"]="cadvisor:v0.55.1" ["gcr.io/cadvisor/cadvisor:v0.55.1"]="cadvisor:v0.55.1"
["prom/node-exporter:v1.10.2"]="node-exporter:v1.10.2" ["prom/node-exporter:v1.10.2"]="node-exporter:v1.10.2"
["gotify/server:v2.9.0"]="gotify:v2.9.0" ["gotify/server:2.9.0"]="gotify:v2.9.0"
) )
# ============================================================================= # =============================================================================

View File

@ -0,0 +1,13 @@
[Unit]
Description=Changemaker Lite database and uploads backup
Documentation=https://docs.cmlite.org/docs/admin/backups/
[Service]
Type=oneshot
User=__USER__
Group=__USER__
WorkingDirectory=__PROJECT_DIR__
ExecStart=__PROJECT_DIR__/scripts/backup.sh --retention 30
TimeoutStartSec=600
StandardOutput=journal
StandardError=journal

View File

@ -0,0 +1,10 @@
[Unit]
Description=Daily Changemaker Lite backup
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target