From 1a0cbe53a1c9c9dfc26016243a91761a604a7b86 Mon Sep 17 00:00:00 2001 From: Noel Date: Sun, 19 Jul 2026 16:27:31 -0700 Subject: [PATCH 1/2] add service umami --- README.md | 1 + services/umami/.env | 34 ++++++++++++ services/umami/README.md | 58 ++++++++++++++++++++ services/umami/compose.yaml | 104 ++++++++++++++++++++++++++++++++++++ 4 files changed, 197 insertions(+) create mode 100644 services/umami/.env create mode 100644 services/umami/README.md create mode 100644 services/umami/compose.yaml diff --git a/README.md b/README.md index 07ab350f..4081fb10 100644 --- a/README.md +++ b/README.md @@ -207,6 +207,7 @@ ScaleTail provides ready-to-run [Docker Compose](https://docs.docker.com/compose | 🛰️ **Beszel Agent** | The Beszel Agent collects stats and reports them back to the Hub. | [Details](services/beszel-agent) | | 🔎 **Portracker** | A simple, self-hosted port monitoring and tracking tool for auditing open ports. | [Details](services/portracker) | | 🚀 **Speedtest Tracker** | A self-hosted tool to monitor and log internet speed tests with detailed visualizations. | [Details](services/speedtest-tracker) | +| 📊 **Umami** | An open-source web analytics platform that respects user privacy. | [Details](services/umami) | | 📊 **Uptime Kuma** | A self-hosted monitoring tool like "Uptime Robot". | [Details](services/uptime-kuma) | ### 🏠 Smart Home diff --git a/services/umami/.env b/services/umami/.env new file mode 100644 index 00000000..028ef7b3 --- /dev/null +++ b/services/umami/.env @@ -0,0 +1,34 @@ +#version=1.1 +#URL=https://github.com/tailscale-dev/ScaleTail +#COMPOSE_PROJECT_NAME= # Optional: only use when running multiple deployments on the same infrastructure. + +# Service Configuration +SERVICE=umami # Service name (e.g., adguard). Used as hostname in Tailscale and for container naming (app-${SERVICE}). +IMAGE_URL=ghcr.io/umami-software/umami:postgresql-latest # Docker image URL from container registry (e.g., adguard/adguard-home). + +# Network Configuration +SERVICEPORT=3000 # Port to expose to local network. Uncomment the "ports:" section in compose.yaml to enable. +DNS_SERVER=9.9.9.9 # Preferred DNS server for Tailscale. Uncomment the "dns:" section in compose.yaml to enable. + +# Tailscale Configuration +TS_AUTHKEY= # Auth key from https://tailscale.com/admin/authkeys. See: https://tailscale.com/kb/1085/auth-keys#generate-an-auth-key for instructions. + +# Optional Service variables +# PUID=1000 + +# Time Zone setting for containers +TZ=Europe/Amsterdam # See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones + +APP_SECRET= # REQUIRED - Generate a salt with, e.g. `openssl rand -base64 32` + +# Database Configuration +DB_IMAGE_URL=postgres:15-alpine +DB_NAME=umami +DB_USER=umami +DB_PASSWORD= # REQUIRED + +# The serve config to use. +# 'private' keeps all access to umami within your tailnet +# 'public' exposes the analytics script and api endpoint via funnel so that site that are not on the tailnet can report analytics +SERVE_CONFIG=private +# SERVE_CONFIG=public diff --git a/services/umami/README.md b/services/umami/README.md new file mode 100644 index 00000000..bcc90ee6 --- /dev/null +++ b/services/umami/README.md @@ -0,0 +1,58 @@ +# Umami with Tailscale Sidecar Configuration + +This Docker Compose configuration sets up [Umami](https://github.com/umami-software/umami) with Tailscale as a sidecar +container to keep the app reachable over your Tailnet. + +## Umami + +[Umami](https://github.com/umami-software/umami) is an open-source web analytics platform that respects user privacy. No +cookies, no tracking across sites, no personal data collection. GDPR compliant out of the box. This configuration +leverages Tailscale to securely connect to your Umami dashboards, protecting your analytics data from unauthorized +access. + +## Configuration Overview + +In this setup, the `tailscale-umami` service runs Tailscale, which manages secure networking for Umami. The +`Umami` service utilizes the Tailscale network stack via Docker's `network_mode: service:` configuration. + +## Prerequisites + +- Docker and the Compose plugin, with your user in the `docker` group (or use `sudo`). +- `/dev/net/tun` available on the host and the `NET_ADMIN` capability, both already declared in `compose.yaml`. +- A Tailscale [auth key](https://console.tailscale.com/admin/settings/keys) from the web admin console (**Settings → + Keys → Generate auth key**). Set it to "Pre-Approved" if that option appears. The key is used only for the initial + registration — with `TS_AUTH_ONCE=true` and the persisted `ts/state` volume, restarts reuse the stored node state — so + a single-use key is sufficient. Tagging the device disables key expiry, which avoids re-authentication after the + default 180 days. +- HTTPS certificates [enabled for your Tailnet](https://console.tailscale.com/admin/dns) (**DNS → HTTPS Certificates**). + Tailscale Serve cannot issue a certificate without it, and the container will start but never serve. +- Funnel must be allowed for the node in the tailnet policy if using public mode. + +## Files to check + +Please verify the following files and variables before deploying: + +- `.env` set `TS_AUTHKEY`, `TZ`, `APP_SECRET`, `DB_PASSWORD`, `SERVE_CONFIG` + +## Usage Notes + +By default, `SERVE_CONFIG` is `private`. This keeps the app Tailnet-only, and available on port 443. When `SERVE_CONFIG` +is set to `public`, the `/script.js` and `/api/send` paths are exposed by funnel on port 443, while the rest of the +app is accessible only over the Tailnet on port 8443. Public mode is useful for tracking analytics from websites +whose clients are not on the Tailnet. + +Default credentials for Umami are `admin` / `umami`. Change these default admin / umami password right after the +first login. + +To start collecting data, follow the Umami [documentation](https://docs.umami.is/docs/collect-data) to set up a +website, then embed the tracking script like so: + +```html + + ``` -## References +## Configuration + +- **`SERVE_CONFIG`.** Use `private` when all visitors of your websites are on your Tailnet. Use `public` to collect data from websites with visitors outside your Tailnet. Public mode needs [Funnel](https://tailscale.com/kb/1223/funnel) allowed for the device in your Tailnet policy. Recreate the stack after you change the value. + +## Links - [Umami documentation](https://umami.is/docs) -- [Umami on GitHub](https://github.com/umami-software/umami) +- [Umami source code](https://github.com/umami-software/umami) diff --git a/services/umami/compose.yaml b/services/umami/compose.yaml index 37495f76..802f04a9 100644 --- a/services/umami/compose.yaml +++ b/services/umami/compose.yaml @@ -9,7 +9,7 @@ configs: ts-serve-public: content: | {"TCP": {"443": {"HTTPS": true}, "8443": {"HTTPS": true}}, - "Web": {"$${TS_CERT_DOMAIN}:443": + "Web": {"$${TS_CERT_DOMAIN}:443": {"Handlers": {"/script.js": {"Proxy": "http://127.0.0.1:3000/script.js"}, "/api/send": @@ -20,8 +20,8 @@ configs: "AllowFunnel": {"$${TS_CERT_DOMAIN}:443": true, "$${TS_CERT_DOMAIN}:8443": false}} services: -# Make sure you have updated/checked the .env file with the correct variables. -# All the ${ xx } need to be defined there. +# Make sure you have updated/checked the .env file with the correct variables. +# Every variable used in this file must be defined there. # Tailscale Sidecar Configuration tailscale: image: tailscale/tailscale:latest # Image to be used @@ -34,7 +34,7 @@ services: - TS_USERSPACE=false - TS_ENABLE_HEALTH_CHECK=true # Enable healthcheck endpoint: "/healthz" - TS_LOCAL_ADDR_PORT=127.0.0.1:41234 # The : for the healthz endpoint -# - TS_ACCEPT_DNS=true # Uncomment when using MagicDNS + #- TS_ACCEPT_DNS=true # Uncomment only if the service must resolve MagicDNS names - this replaces Docker DNS, so Compose service names no longer resolve - TS_AUTH_ONCE=true configs: - source: ts-serve-${SERVE_CONFIG} @@ -47,9 +47,9 @@ services: cap_add: - net_admin # Tailscale requirement #ports: - # - 0.0.0.0:${SERVICEPORT}:${SERVICEPORT} # Binding port ${SERVICE}PORT to the local network - may be removed if only exposure to your Tailnet is required + # - 0.0.0.0:${SERVICEPORT}:${SERVICEPORT} # Binding the service port to the local network - may be removed if only exposure to your Tailnet is required # If any DNS issues arise, use your preferred DNS provider by uncommenting the config below - #dns: + #dns: # - ${DNS_SERVER} healthcheck: test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:41234/healthz"] # Check Tailscale has a Tailnet IP and is operational @@ -59,46 +59,43 @@ services: start_period: 10s # Time to wait before starting health checks restart: always - # umami + # Umami application: image: ${IMAGE_URL} # Image to be used - network_mode: service:tailscale # Sidecar configuration to route ${SERVICE} through Tailscale + network_mode: service:tailscale # Sidecar configuration to route the service through Tailscale container_name: app-${SERVICE} # Name for local container management - environment: # Varibles are delared in .env file. - - PUID=${PUID} - - PGID=${PGID} - - TZ=${TZ} - - DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@database:5432/${DB_NAME} - - DATABASE_TYPE=postgresql - - APP_SECRET=${APP_SECRET} + environment: # Variables are declared in .env file. + - DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD:?Set DB_PASSWORD in .env}@database:5432/${DB_NAME} + - APP_SECRET=${APP_SECRET:?Set APP_SECRET in .env} depends_on: tailscale: condition: service_healthy database: condition: service_healthy healthcheck: - test: [ "CMD", "curl", "-fsS", "http://127.0.0.1:3000/api/heartbeat" ] # Check if ${SERVICE} process is running + test: ["CMD", "curl", "-fsS", "http://127.0.0.1:3000/api/heartbeat"] # Check if the service is responding interval: 1m # How often to perform the check timeout: 10s # Time to wait for the check to succeed retries: 3 # Number of retries before marking as unhealthy start_period: 30s # Time to wait before starting health checks restart: always + # PostgreSQL database database: image: ${DB_IMAGE_URL} container_name: db-${SERVICE} environment: # Variables are declared in .env file. POSTGRES_DB: ${DB_NAME} POSTGRES_USER: ${DB_USER} - POSTGRES_PASSWORD: ${DB_PASSWORD} + POSTGRES_PASSWORD: ${DB_PASSWORD:?Set DB_PASSWORD in .env} # Point PGDATA to a subfolder inside the volume mount PGDATA: /var/lib/postgresql/data/pgdata volumes: - ./${SERVICE}-data/db:/var/lib/postgresql/data - restart: unless-stopped healthcheck: - test: [ 'CMD-SHELL', 'pg_isready -h 127.0.0.1 -U $${POSTGRES_USER} -d $${POSTGRES_DB}' ] + test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] # Check if PostgreSQL accepts connections interval: 5s # How often to perform the check timeout: 3s # Time to wait for the check to succeed retries: 10 # Number of retries before marking as unhealthy start_period: 10s # Time to wait before starting health checks + restart: always