diff --git a/umami/.dockerignore b/umami/.dockerignore new file mode 100644 index 00000000..9ac9448d --- /dev/null +++ b/umami/.dockerignore @@ -0,0 +1 @@ +.unikraft diff --git a/umami/Dockerfile b/umami/Dockerfile new file mode 100644 index 00000000..a7930c66 --- /dev/null +++ b/umami/Dockerfile @@ -0,0 +1,54 @@ +FROM node:22-alpine AS build + +RUN apk add --no-cache libc6-compat git binutils + +WORKDIR /app + +# Clone pinned Umami release +ARG UMAMI_VERSION=v3.0.3 +RUN git clone --depth 1 --branch ${UMAMI_VERSION} https://github.com/umami-software/umami.git . + +# Install pnpm and dependencies +RUN npm install -g pnpm && pnpm install --frozen-lockfile + +# Copy Docker-specific middleware (disables auth middleware for container use) +RUN cp docker/middleware.ts src/middleware.ts + +# The Unikraft Cloud postgres example does not include the pgcrypto extension. +# PG16+ has gen_random_uuid() built-in, so the extension is not needed. +RUN sed -i 's/CREATE EXTENSION IF NOT EXISTS "pgcrypto";/-- pgcrypto: not needed on PG16+/' \ + prisma/migrations/01_init/migration.sql + +# Dummy DATABASE_URL required for prisma generate during build +ENV DATABASE_URL="postgresql://user:pass@localhost:5432/umami" +ENV NEXT_TELEMETRY_DISABLED=1 + +# Build: prisma client + tracker + geo database + Next.js app +RUN pnpm build-docker + +# Strip node binary (~20 MB savings) +RUN strip /usr/local/bin/node + +FROM scratch + +# Node binary (stripped) +COPY --from=build /usr/local/bin/node /usr/bin/node + +# System libraries +COPY --from=build /lib/ld-musl-x86_64.so.1 /lib/ld-musl-x86_64.so.1 +COPY --from=build /usr/lib/libgcc_s.so.1 /usr/lib/libgcc_s.so.1 +COPY --from=build /usr/lib/libstdc++.so.6 /usr/lib/libstdc++.so.6 + +# SSL certificates (needed for TLS database connections) +COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt + +# Distribution configuration +COPY --from=build /etc/os-release /etc/os-release + +# Next.js standalone output +COPY --from=build /app/.next/standalone /app/ +COPY --from=build /app/.next/static /app/.next/static +COPY --from=build /app/public /app/public + +ENV HOSTNAME=0.0.0.0 +ENV PORT=3000 diff --git a/umami/Kraftfile b/umami/Kraftfile new file mode 100644 index 00000000..f08151b3 --- /dev/null +++ b/umami/Kraftfile @@ -0,0 +1,7 @@ +spec: v0.6 + +runtime: base-compat:latest + +rootfs: ./Dockerfile + +cmd: ["/usr/bin/node", "/app/server.js"] diff --git a/umami/README.md b/umami/README.md new file mode 100644 index 00000000..810fc74f --- /dev/null +++ b/umami/README.md @@ -0,0 +1,186 @@ +# Umami Web Analytics + +This guide shows you how to deploy [Umami](https://umami.is), an open-source, privacy-focused web analytics platform. + +Umami requires PostgreSQL. +This example deploys the [postgres](../postgres/) example as the database backend and runs Umami as a Next.js standalone application. + +To run it, follow these steps: + +1. Install the CLI and a container runtime engine, for example [Docker](https://docs.docker.com/engine/install/). + Use the [unikraft CLI](https://unikraft.com/docs/cli/unikraft) or the legacy [kraft CLI](https://unikraft.org/docs/cli/install). + +2. Clone the [`examples` repository](https://github.com/unikraft-cloud/examples) and `cd` into the `examples/umami/` directory: + +```bash +git clone https://github.com/unikraft-cloud/examples +cd examples/umami/ +``` + +Make sure to log into Unikraft Cloud and pick a [metro](https://unikraft.com/docs/platform/metros) close to you. +This guide uses `fra` (Frankfurt, 🇩🇪): + +```bash title="unikraft" +unikraft login +``` + +or + +```bash title="kraft" +# Set Unikraft Cloud access token +export UKC_TOKEN=token +# Set metro to Frankfurt, DE +export UKC_METRO=fra +``` + +## Step 1: Deploy PostgreSQL + +First, deploy a PostgreSQL instance using the [postgres](../postgres/) example: + +```bash title="unikraft" +cd ../postgres/ +unikraft build . --output /postgres:latest +unikraft run --metro=fra -p 5432:5432/tls -m 1536M -e POSTGRES_PASSWORD= -e POSTGRES_DB=umami --scale-to-zero=off /postgres:latest +``` + +or + +```bash title="kraft" +cd ../postgres/ +kraft cloud deploy -p 5432:5432/tls -M 1G -e POSTGRES_PASSWORD= -e POSTGRES_DB=umami --scale-to-zero off . +``` + +Note the service FQDN from the output (e.g., `young-thunder-fbafrsxj.fra.unikraft.app`). + +## Step 2: Run Database Migrations + +Umami requires database tables to be created before first use. +Run migrations from your local machine using the Prisma CLI: + +```bash +# Clone Umami locally (only needed for the Prisma schema and migrations) +git clone --depth 1 --branch v3.0.3 https://github.com/umami-software/umami.git /tmp/umami-migrate +cd /tmp/umami-migrate + +# Install dependencies +npm install -g pnpm && pnpm install --frozen-lockfile + +# Patch pgcrypto (not available in the Unikraft postgres image, not needed on PG16+) +# On macOS, use: sed -i '' 's/...' instead +sed -i 's/CREATE EXTENSION IF NOT EXISTS "pgcrypto";/-- pgcrypto: not needed on PG16+/' \ + prisma/migrations/01_init/migration.sql + +# Run migrations +DATABASE_URL="postgresql://postgres:@:5432/umami?sslmode=require" \ + npx prisma migrate deploy +``` + +> **Note:** Use `?sslmode=require` when connecting over the public FQDN. + +## Step 3: Deploy Umami + +Return to the `umami/` example directory and deploy: + +```bash title="unikraft" +cd ../umami/ +unikraft build . --output /umami:latest +unikraft run --metro=fra \ + -p 443:3000/tls+http \ + -m 1536M \ + -e DATABASE_URL="postgresql://postgres:@:5432/umami?sslmode=require" \ + -e APP_SECRET="$(openssl rand -hex 32)" \ + -e DISABLE_TELEMETRY=1 \ + /umami:latest +``` + +or + +```bash title="kraft" +cd ../umami/ +kraft cloud deploy \ + -p 443:3000 \ + -M 1536 \ + -e DATABASE_URL="postgresql://postgres:@:5432/umami?sslmode=require" \ + -e APP_SECRET="$(openssl rand -hex 32)" \ + -e DISABLE_TELEMETRY=1 \ + . +``` + +The output shows the instance address and other details: + +```ansi +[●] Deployed successfully! + │ + ├────────── name: umami-f7k2x + ├────────── uuid: c742ae33-d502-481c-b691-7732f00abf0c + ├───────── state: starting + ├──────── domain: https://blue-bush-59v2y072.fra.unikraft.app + ├───────── image: umami@sha256:d7f3221eee2ecbd8d90fcebe97eb7dc7f5989ee95c4cedd4a9fe728a709890b0 + ├──────── memory: 1536 MiB + ├─────── service: blue-bush-59v2y072 + ├── private fqdn: umami-f7k2x.internal + ├──── private ip: 10.0.8.193 + └────────── args: /usr/bin/node /app/server.js +``` + +Open the URL in a browser and log in with the default credentials: + +| Username | Password | +|----------|----------| +| `admin` | `umami` | + +**Change the default password immediately after first login.** + +## Environment Variables + +| Variable | Required | Description | +|----------|----------|-------------| +| `DATABASE_URL` | Yes | PostgreSQL connection string | +| `APP_SECRET` | Recommended | Secret for session encryption. Auto-generated if not set, but won't persist across restarts. | +| `DISABLE_TELEMETRY` | No | Set to `1` to disable Umami's telemetry | +| `COLLECT_API_ENDPOINT` | No | Custom tracker endpoint path (replaces `/api/send`) | + +## Upgrading + +To upgrade to a new Umami release: + +1. Update `UMAMI_VERSION` in the `Dockerfile` (e.g., `v3.0.3` to `v3.1.0`) +2. Run database migrations again (Step 2) with the new version +3. Redeploy (Step 3) + +Migrations are idempotent; already-applied migrations are skipped. + +## Notes + +- **Image size**: The `FROM scratch` image is ~386 MB (including GeoIP data for visitor location tracking). Umami requires at least 1536 MiB of memory to unpack the initramfs and run. To reduce the image by ~54 MB (and lower the minimum memory to 1024 MiB), add `rm -rf /app/.next/standalone/geo` to the `Dockerfile` build stage. This disables visitor location tracking. +- **pgcrypto**: The Unikraft Cloud postgres example does not include the `pgcrypto` extension. This is not a problem because PostgreSQL 16+ provides `gen_random_uuid()` as a built-in function. The migration patch in Step 2 and in the `Dockerfile` comments out the `CREATE EXTENSION` statement. + +## Cleanup + +```bash title="unikraft" +unikraft instances delete +unikraft instances delete +``` + +or + +```bash title="kraft" +kraft cloud instance remove +kraft cloud instance remove +``` + +## Learn more + +Use the `--help` option for detailed information on using Unikraft Cloud: + +```bash title="unikraft" +unikraft --help +``` + +or + +```bash title="kraft" +kraft cloud --help +``` + +Or visit the [CLI Reference](https://unikraft.com/docs/cli/unikraft) or the [legacy CLI Reference](https://unikraft.com/docs/cli/kraft/overview).