From fa6cb06cb59ebc583469e10790ee38a241f40bf7 Mon Sep 17 00:00:00 2001 From: Kartik Jain Date: Sat, 18 Jul 2026 02:55:59 +0530 Subject: [PATCH] feat(umami): add Umami analytics installer Self-contained install (Umami app + PostgreSQL 16) following the standard install.sh/domain.sh + shared caddy-setup pattern. App bound to 127.0.0.1:3000 and fronted by Caddy over HTTPS. APP_SECRET and DB password are generated once and persisted across re-runs. --- umami/README.md | 22 +++++++++++++++ umami/domain.sh | 27 ++++++++++++++++++ umami/install.sh | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 umami/README.md create mode 100755 umami/domain.sh create mode 100755 umami/install.sh diff --git a/umami/README.md b/umami/README.md new file mode 100644 index 0000000..80f9096 --- /dev/null +++ b/umami/README.md @@ -0,0 +1,22 @@ +Steps to install [Umami](https://umami.is) (privacy-friendly web analytics) on [Excloud](https://excloud.in) + +### Minimum Requirements +- Instance: `t1.small` +- Disk: `8GB` + +### Overview +Self-contained deployment: the Umami app plus a PostgreSQL 16 database. A random +`APP_SECRET` and database password are generated on first install and persisted +to `.app_secret` / `.database_password` so re-runs stay stable. + +The app is bound to `127.0.0.1:3000` and served publicly over HTTPS by Caddy. + +### Install +```bash +bash install.sh sub.example.com +``` + +### First login +Umami ships with a default admin account — **change the password immediately**: +- Username: `admin` +- Password: `umami` diff --git a/umami/domain.sh b/umami/domain.sh new file mode 100755 index 0000000..79a18cc --- /dev/null +++ b/umami/domain.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +APP_NAME="umami" +APP_DIR="/var/excloud/apps" +APP_UPSTREAM_PORT="${EXC_APP_UPSTREAM_PORT:-3000}" + +DOMAIN="${1}" + +if [ -z "${DOMAIN}" ]; then + echo "Error: URL argument is required. Example:" >&2 + echo "domain.sh sub.example.com" >&2 + exit 1 +fi + +COMPOSE_FILE="${APP_DIR}/${APP_NAME}/docker-compose.yml" + +source /var/excloud/scripts/caddy-setup.sh + +# Umami reads no host/base-url env, so there is nothing to rewrite on a domain +# change — just point Caddy at the running container. +if is_app_ready "$APP_DIR/$APP_NAME"; then + switch_domain "$DOMAIN" "$APP_UPSTREAM_PORT" "$APP_DIR/$APP_NAME" +else + setup_initializing_page "$DOMAIN" "$APP_NAME" "$APP_DIR/$APP_NAME" + docker compose -f "${COMPOSE_FILE}" up -d --remove-orphans + wait_and_switch_to_proxy "$DOMAIN" "$APP_UPSTREAM_PORT" "$APP_DIR/$APP_NAME" & +fi diff --git a/umami/install.sh b/umami/install.sh new file mode 100755 index 0000000..d136e54 --- /dev/null +++ b/umami/install.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +APP_NAME="umami" +APP_DIR="/var/excloud/apps" +SCRIPT_DIR="/var/excloud/scripts" +APP_UPSTREAM_PORT="${EXC_APP_UPSTREAM_PORT:-3000}" + +mkdir -p "${APP_DIR}" +mkdir -p "${SCRIPT_DIR}" + +DOMAIN="${1}" + +if [ -z "${DOMAIN}" ]; then + echo "Error: URL argument is required. Example:" >&2 + echo "install.sh sub.example.com" >&2 + exit 1 +fi + +UMAMI_DIR="${APP_DIR}/${APP_NAME}" +COMPOSE_FILE="${UMAMI_DIR}/docker-compose.yml" +APP_SECRET_FILE="${UMAMI_DIR}/.app_secret" +DB_PASSWORD_FILE="${UMAMI_DIR}/.database_password" + +mkdir -p "${UMAMI_DIR}" +source /var/excloud/scripts/caddy-setup.sh +setup_initializing_page "$DOMAIN" "$APP_NAME" "$UMAMI_DIR" + +# Persist generated secrets so re-runs don't invalidate sessions / break the DB. +if [ ! -f "${APP_SECRET_FILE}" ]; then + openssl rand -hex 32 > "${APP_SECRET_FILE}" +fi +if [ ! -f "${DB_PASSWORD_FILE}" ]; then + openssl rand -hex 32 > "${DB_PASSWORD_FILE}" +fi + +APP_SECRET="$(cat "${APP_SECRET_FILE}")" +DATABASE_PASSWORD="$(cat "${DB_PASSWORD_FILE}")" + +cat > "${COMPOSE_FILE}" <