Vibecode Umami Cloud
track this build5 phases, 15 steps, beginner friendly0%The software is open source, so the cloud subscription is mostly paying for managed hosting, upgrades, and not touching a server.
You are building a lean indie version of Umami Cloud. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Umami Cloud · indie build Build a user-operated Umami installation with verified collection and restoration. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Application | Upstream Umami container | Reuse the maintained application; the work is deployment and operations. | | Storage | PostgreSQL | Use the upstream schema and keep data across container replacement. | | Deployment | Docker Compose; Caddy for product HTTPS | Explicit volumes, ports and configuration can be reviewed and recreated. | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Docker Engine and Compose v2** · Docker Engine has no hosting fee; Desktop licensing depends on your use, and a rented host is billed separately. - Why: The upstream application runs in containers. - Get it: Install Docker Engine and the Compose plugin on Linux using the official instructions. On Windows use Docker Desktop with WSL2, then work inside a Linux project folder. - Verify: docker compose version prints a v2 version and docker info reaches the daemon. - [ ] **Choose and record the upstream release** · Software/edition terms and optional paid features are defined by the upstream project. - Why: The guide must match the software you actually deploy. - Get it: Open https://docs.umami.is/docs/install, choose a stable release compatible with its database requirements, record its tag and resolved digest in versions.txt, and use those values in compose.yaml. Avoid unattended latest-tag updates. - Verify: versions.txt names both application and database versions or states that the pilot uses embedded SQLite. - [ ] **Umami Cloud acceptance sample** · No paid service required for this local step. - Why: This fixture makes the first end-to-end check reproducible. - Get it: Create synthetic sample data before implementation: A user-owned test site with two page loads and one signup-click custom event. Save it under fixtures/ with the expected results in fixtures/README.md; use no real credentials or customer records. - Verify: The fixture files and expected outcomes exist and can be inspected without a vendor account. - [ ] **Confirm the bounded replacement** · No paid service required for this local step. - Why: The full commercial product is larger than this guide. - Get it: The deliverable is a user-operated Umami installation with verified collection and restoration. Read every exclusion below. Indie mode runs locally; any public or shared use requires the product access and deployment gates first. - Verify: You can name the primary workflow and the vendor features this version deliberately leaves out. ## Quick start ```sh docker compose config --quiet ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Changing a database major version is a migration, not a routine image pull. - This is a user-operated Umami installation with verified collection and restoration; full Umami Cloud feature parity, vendor hosting and support are not included. - managed hosting - upgrades - reliability - team/billing convenience - support If one of those is essential to you, that is the reason to keep paying for Umami Cloud, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Umami Cloud The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a user-operated Umami installation with verified collection and restoration as a bounded replacement for Umami Cloud. Build it in phases in the order below. Do not write the whole product in one pass. Finish the current phase, run its Done when checks, fix failures, then continue. Indie mode completes Phases 1-5. Phase 6 is required before shared or public deployment. ### Stack (fixed, do not substitute) - Application: Upstream Umami container. Reuse the maintained application; the work is deployment and operations. - Storage: PostgreSQL. Use the upstream schema and keep data across container replacement. - Deployment: Docker Compose; Caddy for product HTTPS. Explicit volumes, ports and configuration can be reviewed and recreated. ### Data model (create this before Phase 1) - Use the upstream Umami PostgreSQL schema without custom tables. Persist the database, Compose configuration, selected image digest and encrypted deployment secrets. Track a test website id and a named proof event in OPERATIONS.md. - Use stable ids and explicit relationships; add auxiliary tables named by the phases before those phases use them. - Acceptance fixture: A user-owned test site with two page loads and one signup-click custom event. ### Prerequisites and configuration - Docker Engine and Compose v2: Install Docker Engine and the Compose plugin on Linux using the official instructions. On Windows use Docker Desktop with WSL2, then work inside a Linux project folder. Verify: docker compose version prints a v2 version and docker info reaches the daemon. Cost: Docker Engine has no hosting fee; Desktop licensing depends on your use, and a rented host is billed separately. - Choose and record the upstream release: Open https://docs.umami.is/docs/install, choose a stable release compatible with its database requirements, record its tag and resolved digest in versions.txt, and use those values in compose.yaml. Avoid unattended latest-tag updates. Verify: versions.txt names both application and database versions or states that the pilot uses embedded SQLite. Cost: Software/edition terms and optional paid features are defined by the upstream project. - Umami Cloud acceptance sample: Create synthetic sample data before implementation: A user-owned test site with two page loads and one signup-click custom event. Save it under fixtures/ with the expected results in fixtures/README.md; use no real credentials or customer records. Verify: The fixture files and expected outcomes exist and can be inspected without a vendor account. Cost: No paid service required for this local step. - Confirm the bounded replacement: The deliverable is a user-operated Umami installation with verified collection and restoration. Read every exclusion below. Indie mode runs locally; any public or shared use requires the product access and deployment gates first. Verify: You can name the primary workflow and the vendor features this version deliberately leaves out. Cost: No paid service required for this local step. - Configuration: APP_IMAGE, SITE_URL, POSTGRES_PASSWORD, DATABASE_URL. Ship .env.example, replace placeholders and exclude secrets from git. ### Phase 1 · Deployment inventory and safe pilot Build: Prepare the pinned upstream deployment for a user-operated Umami installation with verified collection and restoration. - Read the linked upstream installation guide and create compose.yaml, .env.example, versions.txt and OPERATIONS.md. Map APP_IMAGE and only each service's required environment variables explicitly. Keep database ports internal and map the pilot application port to 127.0.0.1. - Use the upstream Umami PostgreSQL schema without custom tables. Persist the database, Compose configuration, selected image digest and encrypted deployment secrets. Track a test website id and a named proof event in OPERATIONS.md. Name each volume and its mount path in OPERATIONS.md; never use ephemeral container storage for the only copy. Create .env manually from .env.example and replace placeholders before starting. Commands, after creating the named files: `docker compose config --quiet`. - Create a private fixtures/ folder containing: A user-owned test site with two page loads and one signup-click custom event. Create a separate backup destination and an isolated restore-project name; do not reuse live volumes in restore exercises. - Trap: A valid Compose file proves configuration syntax only, not application health. Done when: docker compose config --quiet succeeds with no unresolved required variables. The documented volume list includes every persistent asset and no secret is committed. Do not build yet: Do not start isolated upstream deployment until these checks pass. ### Phase 2 · Isolated upstream deployment Build: Isolated upstream deployment for Umami Cloud: Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. - Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. - Set DATABASE_URL using the internal database service name and change the initial administrator password immediately. - Use A user-owned test site with two page loads and one signup-click custom event. Check: The dashboard opens locally and the original default password no longer works. Then check: Restarting both containers preserves the administrator and registered website. Record commands, observed results and the date in OPERATIONS.md. - Trap: Do not implement a second analytics engine or edit upstream database tables. Done when: The dashboard opens locally and the original default password no longer works. Restarting both containers preserves the administrator and registered website. Do not build yet: Do not start prove collection until these checks pass. ### Phase 3 · Prove collection Build: Prove collection for Umami Cloud: Register the owned test website and use the exact tracking snippet generated by this installed version. - Register the owned test website and use the exact tracking snippet generated by this installed version. - Generate the fixture pageviews and named event; inspect the network response and dashboard before declaring collection healthy. - Use A user-owned test site with two page loads and one signup-click custom event. Check: The named signup-click event appears for the correct website. Then check: No event is attributed to a second website id in the fixture. Record commands, observed results and the date in OPERATIONS.md. - Trap: Collection blocked by consent settings or a browser extension is not evidence that the server is down. Done when: The named signup-click event appears for the correct website. No event is attributed to a second website id in the fixture. Do not build yet: Do not start restore and upgrade drill until these checks pass. ### Phase 4 · Restore and upgrade drill Build: Restore and upgrade drill for Umami Cloud: Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. - Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. - Restore into a separate Compose project with a new volume, compare websites/events and rehearse an upgrade there before production. - Use A user-owned test site with two page loads and one signup-click custom event. Check: The restored dashboard contains the test website and proof event. Then check: The restore exercise touches no live database volume and records its date. Record commands, observed results and the date in OPERATIONS.md. - Trap: Changing a database major version is a migration, not a routine image pull. Done when: The restored dashboard contains the test website and proof event. The restore exercise touches no live database volume and records its date. Do not build yet: Do not start portable delivery and clean-start proof until these checks pass. ### Phase 5 · Portable delivery and clean-start proof Build: Finish the Umami Cloud replacement with a documented first-run path and a recoverable export. - Document prerequisites, every configuration variable, data locations and the three domain workflows: Isolated upstream deployment; Prove collection; Restore and upgrade drill. Include the exclusions, actual costs and source check date 2026-09-05. Commands shown earlier that reference project files are files the builder must implement before running them. - Use a PostgreSQL logical dump plus the Compose file, image manifest and securely stored deployment secrets. Restore into a separate empty directory or isolated deployment, with all outbound actions and schedules disabled. Recreate this proof: A user-owned test site with two page loads and one signup-click custom event. Compare ids/counts and open the result before recording the restore date. Never test a restore over the only live copy. - Use a new folder and only README instructions to reach the fixture outcome. Verify upstream login, persistence, proof output and restore. Record exactly what passed; do not mark unrun checks complete. Commands, after creating the named files: `docker compose ps`. - Trap: Export without an import or restore rehearsal is not verified portability. Done when: A new folder reaches the documented fixture outcome using only README instructions. A backup restored in isolation preserves the proof data and can be opened. Do not build yet: Do not start operate and share with access controls until these checks pass. ### Phase 6 · Operate and share with access controls (product-builder only) Build: Product-builder only: make a user-operated Umami installation with verified collection and restoration operable for its stated audience before allowing remote access. - Use upstream authentication, replace default credentials, restrict the editor/admin surface and inventory public ingestion/webhook paths. Review upstream edition and license boundaries before offering this as a service. - Put Caddy HTTPS in front of the upstream app, keep database and management ports private and configure public URLs from the chosen domain. For n8n, rehearse upstream PostgreSQL migration/import and recovery before claiming production readiness; disable any unused AI sandbox services. - Add a scheduled encrypted off-host backup using this procedure: Use a PostgreSQL logical dump plus the Compose file, image manifest and securely stored deployment secrets. Monitor health, disk space, failed jobs and backup age. Stop the service to test an alert; rehearse recovery with outbound effects disabled and record recovery time. Rotate any exposed credentials and require reconciliation before resuming queues. - Trap: Product mode is additional engineering, not a switch that makes a local prototype production-ready. Done when: An unauthenticated visitor cannot read private records, download private files or mutate the admin surface. A viewer cannot mutate data by making a direct request, and guessed ids cannot cross ownership boundaries. Stopping the service triggers the configured alert and the latest off-host backup restores the fixture in isolation. Do not build yet: Do not add new providers or vendor-scale features until the release gates are recorded. ### Out of scope (and why) - Changing a database major version is a migration, not a routine image pull. - This is a user-operated Umami installation with verified collection and restoration; full Umami Cloud feature parity, vendor hosting and support are not included. - managed hosting: retain the paid product if this capability is essential; it is outside this bounded build. - upgrades: retain the paid product if this capability is essential; it is outside this bounded build. - reliability: retain the paid product if this capability is essential; it is outside this bounded build. - team/billing convenience: retain the paid product if this capability is essential; it is outside this bounded build. - support: retain the paid product if this capability is essential; it is outside this bounded build. ### README must contain - Exact install, configure, initialize, start, test and recovery commands for a clean folder. - The schema, fixture, acceptance results, private data locations and export format. - Every prerequisite and secret source, actual running costs and deliberately excluded capabilities. - An honest distinction between tests actually run and checks still awaiting real credentials or deployment. - Dependency documentation reviewed 2026-09-05: https://docs.umami.is/docs/install ; https://www.postgresql.org/docs/current/backup-dump.html. Recheck before upgrading; freeze the versions actually tested. ===== AGENTS.md ===== # Agent instructions · Umami Cloud indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Upstream Umami container, PostgreSQL, Docker Compose; Caddy for product HTTPS. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - A valid Compose file proves configuration syntax only, not application health. - Do not implement a second analytics engine or edit upstream database tables. - Collection blocked by consent settings or a browser extension is not evidence that the server is down. - Changing a database major version is a migration, not a routine image pull. - Export without an import or restore rehearsal is not verified portability. ===== BUILD_PLAN.md ===== # Build plan · Umami Cloud Build a user-operated Umami installation with verified collection and restoration. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Deployment inventory and safe pilot Prepare the pinned upstream deployment for a user-operated Umami installation with verified collection and restoration. ### Steps 1. Create the deployment files Read the linked upstream installation guide and create compose.yaml, .env.example, versions.txt and OPERATIONS.md. Map APP_IMAGE and only each service's required environment variables explicitly. Keep database ports internal and map the pilot application port to 127.0.0.1. Files: `compose.yaml`, `.env.example`, `versions.txt` 2. Identify every persistent asset Use the upstream Umami PostgreSQL schema without custom tables. Persist the database, Compose configuration, selected image digest and encrypted deployment secrets. Track a test website id and a named proof event in OPERATIONS.md. Name each volume and its mount path in OPERATIONS.md; never use ephemeral container storage for the only copy. Create .env manually from .env.example and replace placeholders before starting. Files: `OPERATIONS.md`, `.gitignore` ```sh docker compose config --quiet ``` 3. Prepare the proof and recovery folder Create a private fixtures/ folder containing: A user-owned test site with two page loads and one signup-click custom event. Create a separate backup destination and an isolated restore-project name; do not reuse live volumes in restore exercises. Files: `fixtures/README.md`, `OPERATIONS.md` ### Done when - [ ] docker compose config --quiet succeeds with no unresolved required variables. - [ ] The documented volume list includes every persistent asset and no secret is committed. ### Watch out - A valid Compose file proves configuration syntax only, not application health. ## Phase 2 · Isolated upstream deployment Isolated upstream deployment for Umami Cloud: Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. ### Steps 1. Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. Files: `compose.yaml`, `OPERATIONS.md` 2. Set DATABASE_URL using the internal database service name and change the initial administrator password immediately. Set DATABASE_URL using the internal database service name and change the initial administrator password immediately. Files: `OPERATIONS.md` 3. Exercise the acceptance fixture Use A user-owned test site with two page loads and one signup-click custom event. Check: The dashboard opens locally and the original default password no longer works. Then check: Restarting both containers preserves the administrator and registered website. Record commands, observed results and the date in OPERATIONS.md. Files: `OPERATIONS.md` ### Done when - [ ] The dashboard opens locally and the original default password no longer works. - [ ] Restarting both containers preserves the administrator and registered website. ### Watch out - Do not implement a second analytics engine or edit upstream database tables. ## Phase 3 · Prove collection Prove collection for Umami Cloud: Register the owned test website and use the exact tracking snippet generated by this installed version. ### Steps 1. Register the owned test website and use the exact tracking snippet generated by this installed version. Register the owned test website and use the exact tracking snippet generated by this installed version. Files: `compose.yaml`, `OPERATIONS.md` 2. Generate the fixture pageviews and named event; inspect the network response and dashboard before declaring collection healthy. Generate the fixture pageviews and named event; inspect the network response and dashboard before declaring collection healthy. Files: `OPERATIONS.md` 3. Exercise the acceptance fixture Use A user-owned test site with two page loads and one signup-click custom event. Check: The named signup-click event appears for the correct website. Then check: No event is attributed to a second website id in the fixture. Record commands, observed results and the date in OPERATIONS.md. Files: `OPERATIONS.md` ### Done when - [ ] The named signup-click event appears for the correct website. - [ ] No event is attributed to a second website id in the fixture. ### Watch out - Collection blocked by consent settings or a browser extension is not evidence that the server is down. ## Phase 4 · Restore and upgrade drill Restore and upgrade drill for Umami Cloud: Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. ### Steps 1. Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. Files: `compose.yaml`, `OPERATIONS.md` 2. Restore into a separate Compose project with a new volume, compare websites/events and rehearse an upgrade there before production. Restore into a separate Compose project with a new volume, compare websites/events and rehearse an upgrade there before production. Files: `OPERATIONS.md` 3. Exercise the acceptance fixture Use A user-owned test site with two page loads and one signup-click custom event. Check: The restored dashboard contains the test website and proof event. Then check: The restore exercise touches no live database volume and records its date. Record commands, observed results and the date in OPERATIONS.md. Files: `OPERATIONS.md` ### Done when - [ ] The restored dashboard contains the test website and proof event. - [ ] The restore exercise touches no live database volume and records its date. ### Watch out - Changing a database major version is a migration, not a routine image pull. ## Phase 5 · Portable delivery and clean-start proof Finish the Umami Cloud replacement with a documented first-run path and a recoverable export. ### Steps 1. Write the user-facing runbook Document prerequisites, every configuration variable, data locations and the three domain workflows: Isolated upstream deployment; Prove collection; Restore and upgrade drill. Include the exclusions, actual costs and source check date 2026-09-05. Commands shown earlier that reference project files are files the builder must implement before running them. Files: `README.md`, `.env.example` 2. Create and rehearse a recoverable backup Use a PostgreSQL logical dump plus the Compose file, image manifest and securely stored deployment secrets. Restore into a separate empty directory or isolated deployment, with all outbound actions and schedules disabled. Recreate this proof: A user-owned test site with two page loads and one signup-click custom event. Compare ids/counts and open the result before recording the restore date. Never test a restore over the only live copy. Files: `OPERATIONS.md`, `backup/README.md` 3. Reproduce the first-run experience Use a new folder and only README instructions to reach the fixture outcome. Verify upstream login, persistence, proof output and restore. Record exactly what passed; do not mark unrun checks complete. Files: `README.md`, `verification.md` ```sh docker compose ps ``` ### Done when - [ ] A new folder reaches the documented fixture outcome using only README instructions. - [ ] A backup restored in isolation preserves the proof data and can be opened. ### Watch out - Export without an import or restore rehearsal is not verified portability. ## Not in this build - Changing a database major version is a migration, not a routine image pull. - This is a user-operated Umami installation with verified collection and restoration; full Umami Cloud feature parity, vendor hosting and support are not included. - managed hosting - upgrades - reliability - team/billing convenience - support ## After v1, if you want it - Extend restore and upgrade drill only after the fixture and recovery checks pass. - Before adding another integration, document its credentials, limits, cost and failure behavior with primary-source links. ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Copy the exact application image reference selected from upstream into compose.yaml via this variable. APP_IMAGE=replace-with-tested-image-and-digest # Required. The loopback URL selected in compose.yaml; replace with your HTTPS origin only after completing product deployment. SITE_URL=http://localhost:3000 # Required · secret. Generate a random password, map it into the database service and URL; keep it outside version control. POSTGRES_PASSWORD=replace-with-random-db-password # Required · secret. Compose internal PostgreSQL connection URL; URL-encode special password characters. DATABASE_URL=postgresql://umami:replace-password@db:5432/umami
You are building a lean indie version of Umami Cloud. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Umami Cloud · indie build Build a user-operated Umami installation with verified collection and restoration. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Application | Upstream Umami container | Reuse the maintained application; the work is deployment and operations. | | Storage | PostgreSQL | Use the upstream schema and keep data across container replacement. | | Deployment | Docker Compose; Caddy for product HTTPS | Explicit volumes, ports and configuration can be reviewed and recreated. | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Docker Engine and Compose v2** · Docker Engine has no hosting fee; Desktop licensing depends on your use, and a rented host is billed separately. - Why: The upstream application runs in containers. - Get it: Install Docker Engine and the Compose plugin on Linux using the official instructions. On Windows use Docker Desktop with WSL2, then work inside a Linux project folder. - Verify: docker compose version prints a v2 version and docker info reaches the daemon. - [ ] **Choose and record the upstream release** · Software/edition terms and optional paid features are defined by the upstream project. - Why: The guide must match the software you actually deploy. - Get it: Open https://docs.umami.is/docs/install, choose a stable release compatible with its database requirements, record its tag and resolved digest in versions.txt, and use those values in compose.yaml. Avoid unattended latest-tag updates. - Verify: versions.txt names both application and database versions or states that the pilot uses embedded SQLite. - [ ] **Umami Cloud acceptance sample** · No paid service required for this local step. - Why: This fixture makes the first end-to-end check reproducible. - Get it: Create synthetic sample data before implementation: A user-owned test site with two page loads and one signup-click custom event. Save it under fixtures/ with the expected results in fixtures/README.md; use no real credentials or customer records. - Verify: The fixture files and expected outcomes exist and can be inspected without a vendor account. - [ ] **Confirm the bounded replacement** · No paid service required for this local step. - Why: The full commercial product is larger than this guide. - Get it: The deliverable is a user-operated Umami installation with verified collection and restoration. Read every exclusion below. Indie mode runs locally; any public or shared use requires the product access and deployment gates first. - Verify: You can name the primary workflow and the vendor features this version deliberately leaves out. ## Quick start ```sh docker compose config --quiet ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Changing a database major version is a migration, not a routine image pull. - This is a user-operated Umami installation with verified collection and restoration; full Umami Cloud feature parity, vendor hosting and support are not included. - managed hosting - upgrades - reliability - team/billing convenience - support If one of those is essential to you, that is the reason to keep paying for Umami Cloud, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Umami Cloud The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a user-operated Umami installation with verified collection and restoration as a bounded replacement for Umami Cloud. Build it in phases in the order below. Do not write the whole product in one pass. Finish the current phase, run its Done when checks, fix failures, then continue. Indie mode completes Phases 1-5. Phase 6 is required before shared or public deployment. ### Stack (fixed, do not substitute) - Application: Upstream Umami container. Reuse the maintained application; the work is deployment and operations. - Storage: PostgreSQL. Use the upstream schema and keep data across container replacement. - Deployment: Docker Compose; Caddy for product HTTPS. Explicit volumes, ports and configuration can be reviewed and recreated. ### Data model (create this before Phase 1) - Use the upstream Umami PostgreSQL schema without custom tables. Persist the database, Compose configuration, selected image digest and encrypted deployment secrets. Track a test website id and a named proof event in OPERATIONS.md. - Use stable ids and explicit relationships; add auxiliary tables named by the phases before those phases use them. - Acceptance fixture: A user-owned test site with two page loads and one signup-click custom event. ### Prerequisites and configuration - Docker Engine and Compose v2: Install Docker Engine and the Compose plugin on Linux using the official instructions. On Windows use Docker Desktop with WSL2, then work inside a Linux project folder. Verify: docker compose version prints a v2 version and docker info reaches the daemon. Cost: Docker Engine has no hosting fee; Desktop licensing depends on your use, and a rented host is billed separately. - Choose and record the upstream release: Open https://docs.umami.is/docs/install, choose a stable release compatible with its database requirements, record its tag and resolved digest in versions.txt, and use those values in compose.yaml. Avoid unattended latest-tag updates. Verify: versions.txt names both application and database versions or states that the pilot uses embedded SQLite. Cost: Software/edition terms and optional paid features are defined by the upstream project. - Umami Cloud acceptance sample: Create synthetic sample data before implementation: A user-owned test site with two page loads and one signup-click custom event. Save it under fixtures/ with the expected results in fixtures/README.md; use no real credentials or customer records. Verify: The fixture files and expected outcomes exist and can be inspected without a vendor account. Cost: No paid service required for this local step. - Confirm the bounded replacement: The deliverable is a user-operated Umami installation with verified collection and restoration. Read every exclusion below. Indie mode runs locally; any public or shared use requires the product access and deployment gates first. Verify: You can name the primary workflow and the vendor features this version deliberately leaves out. Cost: No paid service required for this local step. - Configuration: APP_IMAGE, SITE_URL, POSTGRES_PASSWORD, DATABASE_URL. Ship .env.example, replace placeholders and exclude secrets from git. ### Phase 1 · Deployment inventory and safe pilot Build: Prepare the pinned upstream deployment for a user-operated Umami installation with verified collection and restoration. - Read the linked upstream installation guide and create compose.yaml, .env.example, versions.txt and OPERATIONS.md. Map APP_IMAGE and only each service's required environment variables explicitly. Keep database ports internal and map the pilot application port to 127.0.0.1. - Use the upstream Umami PostgreSQL schema without custom tables. Persist the database, Compose configuration, selected image digest and encrypted deployment secrets. Track a test website id and a named proof event in OPERATIONS.md. Name each volume and its mount path in OPERATIONS.md; never use ephemeral container storage for the only copy. Create .env manually from .env.example and replace placeholders before starting. Commands, after creating the named files: `docker compose config --quiet`. - Create a private fixtures/ folder containing: A user-owned test site with two page loads and one signup-click custom event. Create a separate backup destination and an isolated restore-project name; do not reuse live volumes in restore exercises. - Trap: A valid Compose file proves configuration syntax only, not application health. Done when: docker compose config --quiet succeeds with no unresolved required variables. The documented volume list includes every persistent asset and no secret is committed. Do not build yet: Do not start isolated upstream deployment until these checks pass. ### Phase 2 · Isolated upstream deployment Build: Isolated upstream deployment for Umami Cloud: Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. - Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. - Set DATABASE_URL using the internal database service name and change the initial administrator password immediately. - Use A user-owned test site with two page loads and one signup-click custom event. Check: The dashboard opens locally and the original default password no longer works. Then check: Restarting both containers preserves the administrator and registered website. Record commands, observed results and the date in OPERATIONS.md. - Trap: Do not implement a second analytics engine or edit upstream database tables. Done when: The dashboard opens locally and the original default password no longer works. Restarting both containers preserves the administrator and registered website. Do not build yet: Do not start prove collection until these checks pass. ### Phase 3 · Prove collection Build: Prove collection for Umami Cloud: Register the owned test website and use the exact tracking snippet generated by this installed version. - Register the owned test website and use the exact tracking snippet generated by this installed version. - Generate the fixture pageviews and named event; inspect the network response and dashboard before declaring collection healthy. - Use A user-owned test site with two page loads and one signup-click custom event. Check: The named signup-click event appears for the correct website. Then check: No event is attributed to a second website id in the fixture. Record commands, observed results and the date in OPERATIONS.md. - Trap: Collection blocked by consent settings or a browser extension is not evidence that the server is down. Done when: The named signup-click event appears for the correct website. No event is attributed to a second website id in the fixture. Do not build yet: Do not start restore and upgrade drill until these checks pass. ### Phase 4 · Restore and upgrade drill Build: Restore and upgrade drill for Umami Cloud: Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. - Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. - Restore into a separate Compose project with a new volume, compare websites/events and rehearse an upgrade there before production. - Use A user-owned test site with two page loads and one signup-click custom event. Check: The restored dashboard contains the test website and proof event. Then check: The restore exercise touches no live database volume and records its date. Record commands, observed results and the date in OPERATIONS.md. - Trap: Changing a database major version is a migration, not a routine image pull. Done when: The restored dashboard contains the test website and proof event. The restore exercise touches no live database volume and records its date. Do not build yet: Do not start portable delivery and clean-start proof until these checks pass. ### Phase 5 · Portable delivery and clean-start proof Build: Finish the Umami Cloud replacement with a documented first-run path and a recoverable export. - Document prerequisites, every configuration variable, data locations and the three domain workflows: Isolated upstream deployment; Prove collection; Restore and upgrade drill. Include the exclusions, actual costs and source check date 2026-09-05. Commands shown earlier that reference project files are files the builder must implement before running them. - Use a PostgreSQL logical dump plus the Compose file, image manifest and securely stored deployment secrets. Restore into a separate empty directory or isolated deployment, with all outbound actions and schedules disabled. Recreate this proof: A user-owned test site with two page loads and one signup-click custom event. Compare ids/counts and open the result before recording the restore date. Never test a restore over the only live copy. - Use a new folder and only README instructions to reach the fixture outcome. Verify upstream login, persistence, proof output and restore. Record exactly what passed; do not mark unrun checks complete. Commands, after creating the named files: `docker compose ps`. - Trap: Export without an import or restore rehearsal is not verified portability. Done when: A new folder reaches the documented fixture outcome using only README instructions. A backup restored in isolation preserves the proof data and can be opened. Do not build yet: Do not start operate and share with access controls until these checks pass. ### Phase 6 · Operate and share with access controls (product-builder only) Build: Product-builder only: make a user-operated Umami installation with verified collection and restoration operable for its stated audience before allowing remote access. - Use upstream authentication, replace default credentials, restrict the editor/admin surface and inventory public ingestion/webhook paths. Review upstream edition and license boundaries before offering this as a service. - Put Caddy HTTPS in front of the upstream app, keep database and management ports private and configure public URLs from the chosen domain. For n8n, rehearse upstream PostgreSQL migration/import and recovery before claiming production readiness; disable any unused AI sandbox services. - Add a scheduled encrypted off-host backup using this procedure: Use a PostgreSQL logical dump plus the Compose file, image manifest and securely stored deployment secrets. Monitor health, disk space, failed jobs and backup age. Stop the service to test an alert; rehearse recovery with outbound effects disabled and record recovery time. Rotate any exposed credentials and require reconciliation before resuming queues. - Trap: Product mode is additional engineering, not a switch that makes a local prototype production-ready. Done when: An unauthenticated visitor cannot read private records, download private files or mutate the admin surface. A viewer cannot mutate data by making a direct request, and guessed ids cannot cross ownership boundaries. Stopping the service triggers the configured alert and the latest off-host backup restores the fixture in isolation. Do not build yet: Do not add new providers or vendor-scale features until the release gates are recorded. ### Out of scope (and why) - Changing a database major version is a migration, not a routine image pull. - This is a user-operated Umami installation with verified collection and restoration; full Umami Cloud feature parity, vendor hosting and support are not included. - managed hosting: retain the paid product if this capability is essential; it is outside this bounded build. - upgrades: retain the paid product if this capability is essential; it is outside this bounded build. - reliability: retain the paid product if this capability is essential; it is outside this bounded build. - team/billing convenience: retain the paid product if this capability is essential; it is outside this bounded build. - support: retain the paid product if this capability is essential; it is outside this bounded build. ### README must contain - Exact install, configure, initialize, start, test and recovery commands for a clean folder. - The schema, fixture, acceptance results, private data locations and export format. - Every prerequisite and secret source, actual running costs and deliberately excluded capabilities. - An honest distinction between tests actually run and checks still awaiting real credentials or deployment. - Dependency documentation reviewed 2026-09-05: https://docs.umami.is/docs/install ; https://www.postgresql.org/docs/current/backup-dump.html. Recheck before upgrading; freeze the versions actually tested. ===== AGENTS.md ===== # Agent instructions · Umami Cloud indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Upstream Umami container, PostgreSQL, Docker Compose; Caddy for product HTTPS. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - A valid Compose file proves configuration syntax only, not application health. - Do not implement a second analytics engine or edit upstream database tables. - Collection blocked by consent settings or a browser extension is not evidence that the server is down. - Changing a database major version is a migration, not a routine image pull. - Export without an import or restore rehearsal is not verified portability. ===== BUILD_PLAN.md ===== # Build plan · Umami Cloud Build a user-operated Umami installation with verified collection and restoration. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Deployment inventory and safe pilot Prepare the pinned upstream deployment for a user-operated Umami installation with verified collection and restoration. ### Steps 1. Create the deployment files Read the linked upstream installation guide and create compose.yaml, .env.example, versions.txt and OPERATIONS.md. Map APP_IMAGE and only each service's required environment variables explicitly. Keep database ports internal and map the pilot application port to 127.0.0.1. Files: `compose.yaml`, `.env.example`, `versions.txt` 2. Identify every persistent asset Use the upstream Umami PostgreSQL schema without custom tables. Persist the database, Compose configuration, selected image digest and encrypted deployment secrets. Track a test website id and a named proof event in OPERATIONS.md. Name each volume and its mount path in OPERATIONS.md; never use ephemeral container storage for the only copy. Create .env manually from .env.example and replace placeholders before starting. Files: `OPERATIONS.md`, `.gitignore` ```sh docker compose config --quiet ``` 3. Prepare the proof and recovery folder Create a private fixtures/ folder containing: A user-owned test site with two page loads and one signup-click custom event. Create a separate backup destination and an isolated restore-project name; do not reuse live volumes in restore exercises. Files: `fixtures/README.md`, `OPERATIONS.md` ### Done when - [ ] docker compose config --quiet succeeds with no unresolved required variables. - [ ] The documented volume list includes every persistent asset and no secret is committed. ### Watch out - A valid Compose file proves configuration syntax only, not application health. ## Phase 2 · Isolated upstream deployment Isolated upstream deployment for Umami Cloud: Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. ### Steps 1. Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. Files: `compose.yaml`, `OPERATIONS.md` 2. Set DATABASE_URL using the internal database service name and change the initial administrator password immediately. Set DATABASE_URL using the internal database service name and change the initial administrator password immediately. Files: `OPERATIONS.md` 3. Exercise the acceptance fixture Use A user-owned test site with two page loads and one signup-click custom event. Check: The dashboard opens locally and the original default password no longer works. Then check: Restarting both containers preserves the administrator and registered website. Record commands, observed results and the date in OPERATIONS.md. Files: `OPERATIONS.md` ### Done when - [ ] The dashboard opens locally and the original default password no longer works. - [ ] Restarting both containers preserves the administrator and registered website. ### Watch out - Do not implement a second analytics engine or edit upstream database tables. ## Phase 3 · Prove collection Prove collection for Umami Cloud: Register the owned test website and use the exact tracking snippet generated by this installed version. ### Steps 1. Register the owned test website and use the exact tracking snippet generated by this installed version. Register the owned test website and use the exact tracking snippet generated by this installed version. Files: `compose.yaml`, `OPERATIONS.md` 2. Generate the fixture pageviews and named event; inspect the network response and dashboard before declaring collection healthy. Generate the fixture pageviews and named event; inspect the network response and dashboard before declaring collection healthy. Files: `OPERATIONS.md` 3. Exercise the acceptance fixture Use A user-owned test site with two page loads and one signup-click custom event. Check: The named signup-click event appears for the correct website. Then check: No event is attributed to a second website id in the fixture. Record commands, observed results and the date in OPERATIONS.md. Files: `OPERATIONS.md` ### Done when - [ ] The named signup-click event appears for the correct website. - [ ] No event is attributed to a second website id in the fixture. ### Watch out - Collection blocked by consent settings or a browser extension is not evidence that the server is down. ## Phase 4 · Restore and upgrade drill Restore and upgrade drill for Umami Cloud: Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. ### Steps 1. Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. Files: `compose.yaml`, `OPERATIONS.md` 2. Restore into a separate Compose project with a new volume, compare websites/events and rehearse an upgrade there before production. Restore into a separate Compose project with a new volume, compare websites/events and rehearse an upgrade there before production. Files: `OPERATIONS.md` 3. Exercise the acceptance fixture Use A user-owned test site with two page loads and one signup-click custom event. Check: The restored dashboard contains the test website and proof event. Then check: The restore exercise touches no live database volume and records its date. Record commands, observed results and the date in OPERATIONS.md. Files: `OPERATIONS.md` ### Done when - [ ] The restored dashboard contains the test website and proof event. - [ ] The restore exercise touches no live database volume and records its date. ### Watch out - Changing a database major version is a migration, not a routine image pull. ## Phase 5 · Portable delivery and clean-start proof Finish the Umami Cloud replacement with a documented first-run path and a recoverable export. ### Steps 1. Write the user-facing runbook Document prerequisites, every configuration variable, data locations and the three domain workflows: Isolated upstream deployment; Prove collection; Restore and upgrade drill. Include the exclusions, actual costs and source check date 2026-09-05. Commands shown earlier that reference project files are files the builder must implement before running them. Files: `README.md`, `.env.example` 2. Create and rehearse a recoverable backup Use a PostgreSQL logical dump plus the Compose file, image manifest and securely stored deployment secrets. Restore into a separate empty directory or isolated deployment, with all outbound actions and schedules disabled. Recreate this proof: A user-owned test site with two page loads and one signup-click custom event. Compare ids/counts and open the result before recording the restore date. Never test a restore over the only live copy. Files: `OPERATIONS.md`, `backup/README.md` 3. Reproduce the first-run experience Use a new folder and only README instructions to reach the fixture outcome. Verify upstream login, persistence, proof output and restore. Record exactly what passed; do not mark unrun checks complete. Files: `README.md`, `verification.md` ```sh docker compose ps ``` ### Done when - [ ] A new folder reaches the documented fixture outcome using only README instructions. - [ ] A backup restored in isolation preserves the proof data and can be opened. ### Watch out - Export without an import or restore rehearsal is not verified portability. ## Not in this build - Changing a database major version is a migration, not a routine image pull. - This is a user-operated Umami installation with verified collection and restoration; full Umami Cloud feature parity, vendor hosting and support are not included. - managed hosting - upgrades - reliability - team/billing convenience - support ## After v1, if you want it - Extend restore and upgrade drill only after the fixture and recovery checks pass. - Before adding another integration, document its credentials, limits, cost and failure behavior with primary-source links. ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Copy the exact application image reference selected from upstream into compose.yaml via this variable. APP_IMAGE=replace-with-tested-image-and-digest # Required. The loopback URL selected in compose.yaml; replace with your HTTPS origin only after completing product deployment. SITE_URL=http://localhost:3000 # Required · secret. Generate a random password, map it into the database service and URL; keep it outside version control. POSTGRES_PASSWORD=replace-with-random-db-password # Required · secret. Compose internal PostgreSQL connection URL; URL-encode special password characters. DATABASE_URL=postgresql://umami:replace-password@db:5432/umami
You are building a production product version of Umami Cloud. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== PRODUCT.md ===== # Umami Cloud · product brief ## Problem The software is open source, so the cloud subscription is mostly paying for managed hosting, upgrades, and not touching a server. ## Product outcome An operated version of a user-operated Umami installation with verified collection and restoration, with authorized access, restore evidence and honest scope limits. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - VPS/Docker - database - domain/SSL - backups - update routine ## Explicit non-goals for v1 - Changing a database major version is a migration, not a routine image pull. - This is a user-operated Umami installation with verified collection and restoration; full Umami Cloud feature parity, vendor hosting and support are not included. - managed hosting - upgrades - reliability - team/billing convenience - support ## Success criteria - The dashboard opens locally and the original default password no longer works. - The named signup-click event appears for the correct website. - The restored dashboard contains the test website and proof event. - A private-data authorization check passes using a second identity. - An off-host restore drill has a date, observed result and recovery time in OPERATIONS.md. - README names the excluded vendor capabilities and actual provider/hosting costs. ===== BRIEF.md ===== # Build brief · Umami Cloud The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a user-operated Umami installation with verified collection and restoration as a bounded replacement for Umami Cloud. Build it in phases in the order below. Do not write the whole product in one pass. Finish the current phase, run its Done when checks, fix failures, then continue. Indie mode completes Phases 1-5. Phase 6 is required before shared or public deployment. ### Stack (fixed, do not substitute) - Application: Upstream Umami container. Reuse the maintained application; the work is deployment and operations. - Storage: PostgreSQL. Use the upstream schema and keep data across container replacement. - Deployment: Docker Compose; Caddy for product HTTPS. Explicit volumes, ports and configuration can be reviewed and recreated. ### Data model (create this before Phase 1) - Use the upstream Umami PostgreSQL schema without custom tables. Persist the database, Compose configuration, selected image digest and encrypted deployment secrets. Track a test website id and a named proof event in OPERATIONS.md. - Use stable ids and explicit relationships; add auxiliary tables named by the phases before those phases use them. - Acceptance fixture: A user-owned test site with two page loads and one signup-click custom event. ### Prerequisites and configuration - Docker Engine and Compose v2: Install Docker Engine and the Compose plugin on Linux using the official instructions. On Windows use Docker Desktop with WSL2, then work inside a Linux project folder. Verify: docker compose version prints a v2 version and docker info reaches the daemon. Cost: Docker Engine has no hosting fee; Desktop licensing depends on your use, and a rented host is billed separately. - Choose and record the upstream release: Open https://docs.umami.is/docs/install, choose a stable release compatible with its database requirements, record its tag and resolved digest in versions.txt, and use those values in compose.yaml. Avoid unattended latest-tag updates. Verify: versions.txt names both application and database versions or states that the pilot uses embedded SQLite. Cost: Software/edition terms and optional paid features are defined by the upstream project. - Umami Cloud acceptance sample: Create synthetic sample data before implementation: A user-owned test site with two page loads and one signup-click custom event. Save it under fixtures/ with the expected results in fixtures/README.md; use no real credentials or customer records. Verify: The fixture files and expected outcomes exist and can be inspected without a vendor account. Cost: No paid service required for this local step. - Confirm the bounded replacement: The deliverable is a user-operated Umami installation with verified collection and restoration. Read every exclusion below. Indie mode runs locally; any public or shared use requires the product access and deployment gates first. Verify: You can name the primary workflow and the vendor features this version deliberately leaves out. Cost: No paid service required for this local step. - Configuration: APP_IMAGE, SITE_URL, POSTGRES_PASSWORD, DATABASE_URL. Ship .env.example, replace placeholders and exclude secrets from git. ### Phase 1 · Deployment inventory and safe pilot Build: Prepare the pinned upstream deployment for a user-operated Umami installation with verified collection and restoration. - Read the linked upstream installation guide and create compose.yaml, .env.example, versions.txt and OPERATIONS.md. Map APP_IMAGE and only each service's required environment variables explicitly. Keep database ports internal and map the pilot application port to 127.0.0.1. - Use the upstream Umami PostgreSQL schema without custom tables. Persist the database, Compose configuration, selected image digest and encrypted deployment secrets. Track a test website id and a named proof event in OPERATIONS.md. Name each volume and its mount path in OPERATIONS.md; never use ephemeral container storage for the only copy. Create .env manually from .env.example and replace placeholders before starting. Commands, after creating the named files: `docker compose config --quiet`. - Create a private fixtures/ folder containing: A user-owned test site with two page loads and one signup-click custom event. Create a separate backup destination and an isolated restore-project name; do not reuse live volumes in restore exercises. - Trap: A valid Compose file proves configuration syntax only, not application health. Done when: docker compose config --quiet succeeds with no unresolved required variables. The documented volume list includes every persistent asset and no secret is committed. Do not build yet: Do not start isolated upstream deployment until these checks pass. ### Phase 2 · Isolated upstream deployment Build: Isolated upstream deployment for Umami Cloud: Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. - Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. - Set DATABASE_URL using the internal database service name and change the initial administrator password immediately. - Use A user-owned test site with two page loads and one signup-click custom event. Check: The dashboard opens locally and the original default password no longer works. Then check: Restarting both containers preserves the administrator and registered website. Record commands, observed results and the date in OPERATIONS.md. - Trap: Do not implement a second analytics engine or edit upstream database tables. Done when: The dashboard opens locally and the original default password no longer works. Restarting both containers preserves the administrator and registered website. Do not build yet: Do not start prove collection until these checks pass. ### Phase 3 · Prove collection Build: Prove collection for Umami Cloud: Register the owned test website and use the exact tracking snippet generated by this installed version. - Register the owned test website and use the exact tracking snippet generated by this installed version. - Generate the fixture pageviews and named event; inspect the network response and dashboard before declaring collection healthy. - Use A user-owned test site with two page loads and one signup-click custom event. Check: The named signup-click event appears for the correct website. Then check: No event is attributed to a second website id in the fixture. Record commands, observed results and the date in OPERATIONS.md. - Trap: Collection blocked by consent settings or a browser extension is not evidence that the server is down. Done when: The named signup-click event appears for the correct website. No event is attributed to a second website id in the fixture. Do not build yet: Do not start restore and upgrade drill until these checks pass. ### Phase 4 · Restore and upgrade drill Build: Restore and upgrade drill for Umami Cloud: Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. - Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. - Restore into a separate Compose project with a new volume, compare websites/events and rehearse an upgrade there before production. - Use A user-owned test site with two page loads and one signup-click custom event. Check: The restored dashboard contains the test website and proof event. Then check: The restore exercise touches no live database volume and records its date. Record commands, observed results and the date in OPERATIONS.md. - Trap: Changing a database major version is a migration, not a routine image pull. Done when: The restored dashboard contains the test website and proof event. The restore exercise touches no live database volume and records its date. Do not build yet: Do not start portable delivery and clean-start proof until these checks pass. ### Phase 5 · Portable delivery and clean-start proof Build: Finish the Umami Cloud replacement with a documented first-run path and a recoverable export. - Document prerequisites, every configuration variable, data locations and the three domain workflows: Isolated upstream deployment; Prove collection; Restore and upgrade drill. Include the exclusions, actual costs and source check date 2026-09-05. Commands shown earlier that reference project files are files the builder must implement before running them. - Use a PostgreSQL logical dump plus the Compose file, image manifest and securely stored deployment secrets. Restore into a separate empty directory or isolated deployment, with all outbound actions and schedules disabled. Recreate this proof: A user-owned test site with two page loads and one signup-click custom event. Compare ids/counts and open the result before recording the restore date. Never test a restore over the only live copy. - Use a new folder and only README instructions to reach the fixture outcome. Verify upstream login, persistence, proof output and restore. Record exactly what passed; do not mark unrun checks complete. Commands, after creating the named files: `docker compose ps`. - Trap: Export without an import or restore rehearsal is not verified portability. Done when: A new folder reaches the documented fixture outcome using only README instructions. A backup restored in isolation preserves the proof data and can be opened. Do not build yet: Do not start operate and share with access controls until these checks pass. ### Phase 6 · Operate and share with access controls (product-builder only) Build: Product-builder only: make a user-operated Umami installation with verified collection and restoration operable for its stated audience before allowing remote access. - Use upstream authentication, replace default credentials, restrict the editor/admin surface and inventory public ingestion/webhook paths. Review upstream edition and license boundaries before offering this as a service. - Put Caddy HTTPS in front of the upstream app, keep database and management ports private and configure public URLs from the chosen domain. For n8n, rehearse upstream PostgreSQL migration/import and recovery before claiming production readiness; disable any unused AI sandbox services. - Add a scheduled encrypted off-host backup using this procedure: Use a PostgreSQL logical dump plus the Compose file, image manifest and securely stored deployment secrets. Monitor health, disk space, failed jobs and backup age. Stop the service to test an alert; rehearse recovery with outbound effects disabled and record recovery time. Rotate any exposed credentials and require reconciliation before resuming queues. - Trap: Product mode is additional engineering, not a switch that makes a local prototype production-ready. Done when: An unauthenticated visitor cannot read private records, download private files or mutate the admin surface. A viewer cannot mutate data by making a direct request, and guessed ids cannot cross ownership boundaries. Stopping the service triggers the configured alert and the latest off-host backup restores the fixture in isolation. Do not build yet: Do not add new providers or vendor-scale features until the release gates are recorded. ### Out of scope (and why) - Changing a database major version is a migration, not a routine image pull. - This is a user-operated Umami installation with verified collection and restoration; full Umami Cloud feature parity, vendor hosting and support are not included. - managed hosting: retain the paid product if this capability is essential; it is outside this bounded build. - upgrades: retain the paid product if this capability is essential; it is outside this bounded build. - reliability: retain the paid product if this capability is essential; it is outside this bounded build. - team/billing convenience: retain the paid product if this capability is essential; it is outside this bounded build. - support: retain the paid product if this capability is essential; it is outside this bounded build. ### README must contain - Exact install, configure, initialize, start, test and recovery commands for a clean folder. - The schema, fixture, acceptance results, private data locations and export format. - Every prerequisite and secret source, actual running costs and deliberately excluded capabilities. - An honest distinction between tests actually run and checks still awaiting real credentials or deployment. - Dependency documentation reviewed 2026-09-05: https://docs.umami.is/docs/install ; https://www.postgresql.org/docs/current/backup-dump.html. Recheck before upgrading; freeze the versions actually tested. ===== ARCHITECTURE.md ===== # Architecture · Umami Cloud ## Stack | Part | Choice | Why | | --- | --- | --- | | Application | Upstream Umami container | Reuse the maintained application; the work is deployment and operations. | | Storage | PostgreSQL | Use the upstream schema and keep data across container replacement. | | Deployment | Docker Compose; Caddy for product HTTPS | Explicit volumes, ports and configuration can be reviewed and recreated. | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Data and identity | Use the upstream Umami PostgreSQL schema without custom tables. Persist the database, Compose configuration, selected image digest and encrypted deployment secrets. Track a test website id and a named proof event in OPERATIONS.md. | Follow upstream migration procedures and verify a restored fixture before switching stores. | | Isolated upstream deployment | Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. | Keep the acceptance contract: The dashboard opens locally and the original default password no longer works. Replace only this module after replaying its fixture; preserve ids and existing records. | | Prove collection | Register the owned test website and use the exact tracking snippet generated by this installed version. | Keep the acceptance contract: The named signup-click event appears for the correct website. Replace only this module after replaying its fixture; preserve ids and existing records. | | Restore and upgrade drill | Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. | Keep the acceptance contract: The restored dashboard contains the test website and proof event. Replace only this module after replaying its fixture; preserve ids and existing records. | | Operations | Use a PostgreSQL logical dump plus the Compose file, image manifest and securely stored deployment secrets. | Change host or backup destination only after an isolated restore and authorization check. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `APP_IMAGE` · required · Copy the exact application image reference selected from upstream into compose.yaml via this variable. - `SITE_URL` · required · The loopback URL selected in compose.yaml; replace with your HTTPS origin only after completing product deployment. - `POSTGRES_PASSWORD` · required, secret · Generate a random password, map it into the database service and URL; keep it outside version control. - `DATABASE_URL` · required, secret · Compose internal PostgreSQL connection URL; URL-encode special password characters. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it. ===== AGENTS.md ===== # Agent instructions · Umami Cloud product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Upstream Umami container, PostgreSQL, Docker Compose; Caddy for product HTTPS. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - A valid Compose file proves configuration syntax only, not application health. - Do not implement a second analytics engine or edit upstream database tables. - Collection blocked by consent settings or a browser extension is not evidence that the server is down. - Changing a database major version is a migration, not a routine image pull. - Export without an import or restore rehearsal is not verified portability. - Product mode is additional engineering, not a switch that makes a local prototype production-ready. ===== MILESTONES.md ===== # Delivery milestones · Umami Cloud Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Deployment inventory and safe pilot Prepare the pinned upstream deployment for a user-operated Umami installation with verified collection and restoration. ### Steps 1. Create the deployment files Read the linked upstream installation guide and create compose.yaml, .env.example, versions.txt and OPERATIONS.md. Map APP_IMAGE and only each service's required environment variables explicitly. Keep database ports internal and map the pilot application port to 127.0.0.1. Files: `compose.yaml`, `.env.example`, `versions.txt` 2. Identify every persistent asset Use the upstream Umami PostgreSQL schema without custom tables. Persist the database, Compose configuration, selected image digest and encrypted deployment secrets. Track a test website id and a named proof event in OPERATIONS.md. Name each volume and its mount path in OPERATIONS.md; never use ephemeral container storage for the only copy. Create .env manually from .env.example and replace placeholders before starting. Files: `OPERATIONS.md`, `.gitignore` ```sh docker compose config --quiet ``` 3. Prepare the proof and recovery folder Create a private fixtures/ folder containing: A user-owned test site with two page loads and one signup-click custom event. Create a separate backup destination and an isolated restore-project name; do not reuse live volumes in restore exercises. Files: `fixtures/README.md`, `OPERATIONS.md` ### Done when - [ ] docker compose config --quiet succeeds with no unresolved required variables. - [ ] The documented volume list includes every persistent asset and no secret is committed. ### Watch out - A valid Compose file proves configuration syntax only, not application health. ## M2 · Isolated upstream deployment Isolated upstream deployment for Umami Cloud: Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. ### Steps 1. Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. Files: `compose.yaml`, `OPERATIONS.md` 2. Set DATABASE_URL using the internal database service name and change the initial administrator password immediately. Set DATABASE_URL using the internal database service name and change the initial administrator password immediately. Files: `OPERATIONS.md` 3. Exercise the acceptance fixture Use A user-owned test site with two page loads and one signup-click custom event. Check: The dashboard opens locally and the original default password no longer works. Then check: Restarting both containers preserves the administrator and registered website. Record commands, observed results and the date in OPERATIONS.md. Files: `OPERATIONS.md` ### Done when - [ ] The dashboard opens locally and the original default password no longer works. - [ ] Restarting both containers preserves the administrator and registered website. ### Watch out - Do not implement a second analytics engine or edit upstream database tables. ## M3 · Prove collection Prove collection for Umami Cloud: Register the owned test website and use the exact tracking snippet generated by this installed version. ### Steps 1. Register the owned test website and use the exact tracking snippet generated by this installed version. Register the owned test website and use the exact tracking snippet generated by this installed version. Files: `compose.yaml`, `OPERATIONS.md` 2. Generate the fixture pageviews and named event; inspect the network response and dashboard before declaring collection healthy. Generate the fixture pageviews and named event; inspect the network response and dashboard before declaring collection healthy. Files: `OPERATIONS.md` 3. Exercise the acceptance fixture Use A user-owned test site with two page loads and one signup-click custom event. Check: The named signup-click event appears for the correct website. Then check: No event is attributed to a second website id in the fixture. Record commands, observed results and the date in OPERATIONS.md. Files: `OPERATIONS.md` ### Done when - [ ] The named signup-click event appears for the correct website. - [ ] No event is attributed to a second website id in the fixture. ### Watch out - Collection blocked by consent settings or a browser extension is not evidence that the server is down. ## M4 · Restore and upgrade drill Restore and upgrade drill for Umami Cloud: Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. ### Steps 1. Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. Files: `compose.yaml`, `OPERATIONS.md` 2. Restore into a separate Compose project with a new volume, compare websites/events and rehearse an upgrade there before production. Restore into a separate Compose project with a new volume, compare websites/events and rehearse an upgrade there before production. Files: `OPERATIONS.md` 3. Exercise the acceptance fixture Use A user-owned test site with two page loads and one signup-click custom event. Check: The restored dashboard contains the test website and proof event. Then check: The restore exercise touches no live database volume and records its date. Record commands, observed results and the date in OPERATIONS.md. Files: `OPERATIONS.md` ### Done when - [ ] The restored dashboard contains the test website and proof event. - [ ] The restore exercise touches no live database volume and records its date. ### Watch out - Changing a database major version is a migration, not a routine image pull. ## M5 · Portable delivery and clean-start proof Finish the Umami Cloud replacement with a documented first-run path and a recoverable export. ### Steps 1. Write the user-facing runbook Document prerequisites, every configuration variable, data locations and the three domain workflows: Isolated upstream deployment; Prove collection; Restore and upgrade drill. Include the exclusions, actual costs and source check date 2026-09-05. Commands shown earlier that reference project files are files the builder must implement before running them. Files: `README.md`, `.env.example` 2. Create and rehearse a recoverable backup Use a PostgreSQL logical dump plus the Compose file, image manifest and securely stored deployment secrets. Restore into a separate empty directory or isolated deployment, with all outbound actions and schedules disabled. Recreate this proof: A user-owned test site with two page loads and one signup-click custom event. Compare ids/counts and open the result before recording the restore date. Never test a restore over the only live copy. Files: `OPERATIONS.md`, `backup/README.md` 3. Reproduce the first-run experience Use a new folder and only README instructions to reach the fixture outcome. Verify upstream login, persistence, proof output and restore. Record exactly what passed; do not mark unrun checks complete. Files: `README.md`, `verification.md` ```sh docker compose ps ``` ### Done when - [ ] A new folder reaches the documented fixture outcome using only README instructions. - [ ] A backup restored in isolation preserves the proof data and can be opened. ### Watch out - Export without an import or restore rehearsal is not verified portability. ## M6 · Operate and share with access controls (production only) Product-builder only: make a user-operated Umami installation with verified collection and restoration operable for its stated audience before allowing remote access. ### Steps 1. Establish owner access before exposure Use upstream authentication, replace default credentials, restrict the editor/admin surface and inventory public ingestion/webhook paths. Review upstream edition and license boundaries before offering this as a service. Files: `ARCHITECTURE.md`, `OPERATIONS.md` 2. Deploy a monitored service Put Caddy HTTPS in front of the upstream app, keep database and management ports private and configure public URLs from the chosen domain. For n8n, rehearse upstream PostgreSQL migration/import and recovery before claiming production readiness; disable any unused AI sandbox services. Files: `Caddyfile`, `OPERATIONS.md` 3. Rehearse loss and recovery Add a scheduled encrypted off-host backup using this procedure: Use a PostgreSQL logical dump plus the Compose file, image manifest and securely stored deployment secrets. Monitor health, disk space, failed jobs and backup age. Stop the service to test an alert; rehearse recovery with outbound effects disabled and record recovery time. Rotate any exposed credentials and require reconciliation before resuming queues. Files: `OPERATIONS.md`, `release-checklist.md` ### Done when - [ ] An unauthenticated visitor cannot read private records, download private files or mutate the admin surface. - [ ] A viewer cannot mutate data by making a direct request, and guessed ids cannot cross ownership boundaries. - [ ] Stopping the service triggers the configured alert and the latest off-host backup restores the fixture in isolation. ### Watch out - Product mode is additional engineering, not a switch that makes a local prototype production-ready. ===== OPERATIONS.md ===== # Operations · Umami Cloud ## Backup Use a PostgreSQL logical dump plus the Compose file, image manifest and securely stored deployment secrets. ## Restore Restore into a separate empty directory or isolated deployment, with all outbound actions and schedules disabled. Recreate this proof: A user-owned test site with two page loads and one signup-click custom event. Compare ids/counts and open the result before recording the restore date. Never test a restore over the only live copy. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Check HTTP availability, free disk, backup age and domain failures for Isolated upstream deployment, Prove collection, Restore and upgrade drill. Alert without logging private payloads. ## Incident checklist Pause outbound jobs and public intake as appropriate, preserve redacted diagnostics, restore an isolated verified backup, rotate affected credentials and reconcile uncertain effects before reopening. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] The dashboard opens locally and the original default password no longer works. - [ ] The named signup-click event appears for the correct website. - [ ] The restored dashboard contains the test website and proof event. - [ ] A private-data authorization check passes using a second identity. - [ ] An off-host restore drill has a date, observed result and recovery time in OPERATIONS.md. - [ ] README names the excluded vendor capabilities and actual provider/hosting costs. ## Launch constraint Do not market omitted Umami Cloud capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered. ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Copy the exact application image reference selected from upstream into compose.yaml via this variable. APP_IMAGE=replace-with-tested-image-and-digest # Required. The loopback URL selected in compose.yaml; replace with your HTTPS origin only after completing product deployment. SITE_URL=http://localhost:3000 # Required · secret. Generate a random password, map it into the database service and URL; keep it outside version control. POSTGRES_PASSWORD=replace-with-random-db-password # Required · secret. Compose internal PostgreSQL connection URL; URL-encode special password characters. DATABASE_URL=postgresql://umami:replace-password@db:5432/umami
# Umami Cloud · indie build Build a user-operated Umami installation with verified collection and restoration. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Application | Upstream Umami container | Reuse the maintained application; the work is deployment and operations. | | Storage | PostgreSQL | Use the upstream schema and keep data across container replacement. | | Deployment | Docker Compose; Caddy for product HTTPS | Explicit volumes, ports and configuration can be reviewed and recreated. | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Docker Engine and Compose v2** · Docker Engine has no hosting fee; Desktop licensing depends on your use, and a rented host is billed separately. - Why: The upstream application runs in containers. - Get it: Install Docker Engine and the Compose plugin on Linux using the official instructions. On Windows use Docker Desktop with WSL2, then work inside a Linux project folder. - Verify: docker compose version prints a v2 version and docker info reaches the daemon. - [ ] **Choose and record the upstream release** · Software/edition terms and optional paid features are defined by the upstream project. - Why: The guide must match the software you actually deploy. - Get it: Open https://docs.umami.is/docs/install, choose a stable release compatible with its database requirements, record its tag and resolved digest in versions.txt, and use those values in compose.yaml. Avoid unattended latest-tag updates. - Verify: versions.txt names both application and database versions or states that the pilot uses embedded SQLite. - [ ] **Umami Cloud acceptance sample** · No paid service required for this local step. - Why: This fixture makes the first end-to-end check reproducible. - Get it: Create synthetic sample data before implementation: A user-owned test site with two page loads and one signup-click custom event. Save it under fixtures/ with the expected results in fixtures/README.md; use no real credentials or customer records. - Verify: The fixture files and expected outcomes exist and can be inspected without a vendor account. - [ ] **Confirm the bounded replacement** · No paid service required for this local step. - Why: The full commercial product is larger than this guide. - Get it: The deliverable is a user-operated Umami installation with verified collection and restoration. Read every exclusion below. Indie mode runs locally; any public or shared use requires the product access and deployment gates first. - Verify: You can name the primary workflow and the vendor features this version deliberately leaves out. ## Quick start ```sh docker compose config --quiet ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Changing a database major version is a migration, not a routine image pull. - This is a user-operated Umami installation with verified collection and restoration; full Umami Cloud feature parity, vendor hosting and support are not included. - managed hosting - upgrades - reliability - team/billing convenience - support If one of those is essential to you, that is the reason to keep paying for Umami Cloud, and the README should say so rather than pretend.
# Build brief · Umami Cloud The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a user-operated Umami installation with verified collection and restoration as a bounded replacement for Umami Cloud. Build it in phases in the order below. Do not write the whole product in one pass. Finish the current phase, run its Done when checks, fix failures, then continue. Indie mode completes Phases 1-5. Phase 6 is required before shared or public deployment. ### Stack (fixed, do not substitute) - Application: Upstream Umami container. Reuse the maintained application; the work is deployment and operations. - Storage: PostgreSQL. Use the upstream schema and keep data across container replacement. - Deployment: Docker Compose; Caddy for product HTTPS. Explicit volumes, ports and configuration can be reviewed and recreated. ### Data model (create this before Phase 1) - Use the upstream Umami PostgreSQL schema without custom tables. Persist the database, Compose configuration, selected image digest and encrypted deployment secrets. Track a test website id and a named proof event in OPERATIONS.md. - Use stable ids and explicit relationships; add auxiliary tables named by the phases before those phases use them. - Acceptance fixture: A user-owned test site with two page loads and one signup-click custom event. ### Prerequisites and configuration - Docker Engine and Compose v2: Install Docker Engine and the Compose plugin on Linux using the official instructions. On Windows use Docker Desktop with WSL2, then work inside a Linux project folder. Verify: docker compose version prints a v2 version and docker info reaches the daemon. Cost: Docker Engine has no hosting fee; Desktop licensing depends on your use, and a rented host is billed separately. - Choose and record the upstream release: Open https://docs.umami.is/docs/install, choose a stable release compatible with its database requirements, record its tag and resolved digest in versions.txt, and use those values in compose.yaml. Avoid unattended latest-tag updates. Verify: versions.txt names both application and database versions or states that the pilot uses embedded SQLite. Cost: Software/edition terms and optional paid features are defined by the upstream project. - Umami Cloud acceptance sample: Create synthetic sample data before implementation: A user-owned test site with two page loads and one signup-click custom event. Save it under fixtures/ with the expected results in fixtures/README.md; use no real credentials or customer records. Verify: The fixture files and expected outcomes exist and can be inspected without a vendor account. Cost: No paid service required for this local step. - Confirm the bounded replacement: The deliverable is a user-operated Umami installation with verified collection and restoration. Read every exclusion below. Indie mode runs locally; any public or shared use requires the product access and deployment gates first. Verify: You can name the primary workflow and the vendor features this version deliberately leaves out. Cost: No paid service required for this local step. - Configuration: APP_IMAGE, SITE_URL, POSTGRES_PASSWORD, DATABASE_URL. Ship .env.example, replace placeholders and exclude secrets from git. ### Phase 1 · Deployment inventory and safe pilot Build: Prepare the pinned upstream deployment for a user-operated Umami installation with verified collection and restoration. - Read the linked upstream installation guide and create compose.yaml, .env.example, versions.txt and OPERATIONS.md. Map APP_IMAGE and only each service's required environment variables explicitly. Keep database ports internal and map the pilot application port to 127.0.0.1. - Use the upstream Umami PostgreSQL schema without custom tables. Persist the database, Compose configuration, selected image digest and encrypted deployment secrets. Track a test website id and a named proof event in OPERATIONS.md. Name each volume and its mount path in OPERATIONS.md; never use ephemeral container storage for the only copy. Create .env manually from .env.example and replace placeholders before starting. Commands, after creating the named files: `docker compose config --quiet`. - Create a private fixtures/ folder containing: A user-owned test site with two page loads and one signup-click custom event. Create a separate backup destination and an isolated restore-project name; do not reuse live volumes in restore exercises. - Trap: A valid Compose file proves configuration syntax only, not application health. Done when: docker compose config --quiet succeeds with no unresolved required variables. The documented volume list includes every persistent asset and no secret is committed. Do not build yet: Do not start isolated upstream deployment until these checks pass. ### Phase 2 · Isolated upstream deployment Build: Isolated upstream deployment for Umami Cloud: Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. - Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. - Set DATABASE_URL using the internal database service name and change the initial administrator password immediately. - Use A user-owned test site with two page loads and one signup-click custom event. Check: The dashboard opens locally and the original default password no longer works. Then check: Restarting both containers preserves the administrator and registered website. Record commands, observed results and the date in OPERATIONS.md. - Trap: Do not implement a second analytics engine or edit upstream database tables. Done when: The dashboard opens locally and the original default password no longer works. Restarting both containers preserves the administrator and registered website. Do not build yet: Do not start prove collection until these checks pass. ### Phase 3 · Prove collection Build: Prove collection for Umami Cloud: Register the owned test website and use the exact tracking snippet generated by this installed version. - Register the owned test website and use the exact tracking snippet generated by this installed version. - Generate the fixture pageviews and named event; inspect the network response and dashboard before declaring collection healthy. - Use A user-owned test site with two page loads and one signup-click custom event. Check: The named signup-click event appears for the correct website. Then check: No event is attributed to a second website id in the fixture. Record commands, observed results and the date in OPERATIONS.md. - Trap: Collection blocked by consent settings or a browser extension is not evidence that the server is down. Done when: The named signup-click event appears for the correct website. No event is attributed to a second website id in the fixture. Do not build yet: Do not start restore and upgrade drill until these checks pass. ### Phase 4 · Restore and upgrade drill Build: Restore and upgrade drill for Umami Cloud: Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. - Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. - Restore into a separate Compose project with a new volume, compare websites/events and rehearse an upgrade there before production. - Use A user-owned test site with two page loads and one signup-click custom event. Check: The restored dashboard contains the test website and proof event. Then check: The restore exercise touches no live database volume and records its date. Record commands, observed results and the date in OPERATIONS.md. - Trap: Changing a database major version is a migration, not a routine image pull. Done when: The restored dashboard contains the test website and proof event. The restore exercise touches no live database volume and records its date. Do not build yet: Do not start portable delivery and clean-start proof until these checks pass. ### Phase 5 · Portable delivery and clean-start proof Build: Finish the Umami Cloud replacement with a documented first-run path and a recoverable export. - Document prerequisites, every configuration variable, data locations and the three domain workflows: Isolated upstream deployment; Prove collection; Restore and upgrade drill. Include the exclusions, actual costs and source check date 2026-09-05. Commands shown earlier that reference project files are files the builder must implement before running them. - Use a PostgreSQL logical dump plus the Compose file, image manifest and securely stored deployment secrets. Restore into a separate empty directory or isolated deployment, with all outbound actions and schedules disabled. Recreate this proof: A user-owned test site with two page loads and one signup-click custom event. Compare ids/counts and open the result before recording the restore date. Never test a restore over the only live copy. - Use a new folder and only README instructions to reach the fixture outcome. Verify upstream login, persistence, proof output and restore. Record exactly what passed; do not mark unrun checks complete. Commands, after creating the named files: `docker compose ps`. - Trap: Export without an import or restore rehearsal is not verified portability. Done when: A new folder reaches the documented fixture outcome using only README instructions. A backup restored in isolation preserves the proof data and can be opened. Do not build yet: Do not start operate and share with access controls until these checks pass. ### Phase 6 · Operate and share with access controls (product-builder only) Build: Product-builder only: make a user-operated Umami installation with verified collection and restoration operable for its stated audience before allowing remote access. - Use upstream authentication, replace default credentials, restrict the editor/admin surface and inventory public ingestion/webhook paths. Review upstream edition and license boundaries before offering this as a service. - Put Caddy HTTPS in front of the upstream app, keep database and management ports private and configure public URLs from the chosen domain. For n8n, rehearse upstream PostgreSQL migration/import and recovery before claiming production readiness; disable any unused AI sandbox services. - Add a scheduled encrypted off-host backup using this procedure: Use a PostgreSQL logical dump plus the Compose file, image manifest and securely stored deployment secrets. Monitor health, disk space, failed jobs and backup age. Stop the service to test an alert; rehearse recovery with outbound effects disabled and record recovery time. Rotate any exposed credentials and require reconciliation before resuming queues. - Trap: Product mode is additional engineering, not a switch that makes a local prototype production-ready. Done when: An unauthenticated visitor cannot read private records, download private files or mutate the admin surface. A viewer cannot mutate data by making a direct request, and guessed ids cannot cross ownership boundaries. Stopping the service triggers the configured alert and the latest off-host backup restores the fixture in isolation. Do not build yet: Do not add new providers or vendor-scale features until the release gates are recorded. ### Out of scope (and why) - Changing a database major version is a migration, not a routine image pull. - This is a user-operated Umami installation with verified collection and restoration; full Umami Cloud feature parity, vendor hosting and support are not included. - managed hosting: retain the paid product if this capability is essential; it is outside this bounded build. - upgrades: retain the paid product if this capability is essential; it is outside this bounded build. - reliability: retain the paid product if this capability is essential; it is outside this bounded build. - team/billing convenience: retain the paid product if this capability is essential; it is outside this bounded build. - support: retain the paid product if this capability is essential; it is outside this bounded build. ### README must contain - Exact install, configure, initialize, start, test and recovery commands for a clean folder. - The schema, fixture, acceptance results, private data locations and export format. - Every prerequisite and secret source, actual running costs and deliberately excluded capabilities. - An honest distinction between tests actually run and checks still awaiting real credentials or deployment. - Dependency documentation reviewed 2026-09-05: https://docs.umami.is/docs/install ; https://www.postgresql.org/docs/current/backup-dump.html. Recheck before upgrading; freeze the versions actually tested.
# Agent instructions · Umami Cloud indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Upstream Umami container, PostgreSQL, Docker Compose; Caddy for product HTTPS. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - A valid Compose file proves configuration syntax only, not application health. - Do not implement a second analytics engine or edit upstream database tables. - Collection blocked by consent settings or a browser extension is not evidence that the server is down. - Changing a database major version is a migration, not a routine image pull. - Export without an import or restore rehearsal is not verified portability.
# Build plan · Umami Cloud Build a user-operated Umami installation with verified collection and restoration. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Deployment inventory and safe pilot Prepare the pinned upstream deployment for a user-operated Umami installation with verified collection and restoration. ### Steps 1. Create the deployment files Read the linked upstream installation guide and create compose.yaml, .env.example, versions.txt and OPERATIONS.md. Map APP_IMAGE and only each service's required environment variables explicitly. Keep database ports internal and map the pilot application port to 127.0.0.1. Files: `compose.yaml`, `.env.example`, `versions.txt` 2. Identify every persistent asset Use the upstream Umami PostgreSQL schema without custom tables. Persist the database, Compose configuration, selected image digest and encrypted deployment secrets. Track a test website id and a named proof event in OPERATIONS.md. Name each volume and its mount path in OPERATIONS.md; never use ephemeral container storage for the only copy. Create .env manually from .env.example and replace placeholders before starting. Files: `OPERATIONS.md`, `.gitignore` ```sh docker compose config --quiet ``` 3. Prepare the proof and recovery folder Create a private fixtures/ folder containing: A user-owned test site with two page loads and one signup-click custom event. Create a separate backup destination and an isolated restore-project name; do not reuse live volumes in restore exercises. Files: `fixtures/README.md`, `OPERATIONS.md` ### Done when - [ ] docker compose config --quiet succeeds with no unresolved required variables. - [ ] The documented volume list includes every persistent asset and no secret is committed. ### Watch out - A valid Compose file proves configuration syntax only, not application health. ## Phase 2 · Isolated upstream deployment Isolated upstream deployment for Umami Cloud: Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. ### Steps 1. Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. Files: `compose.yaml`, `OPERATIONS.md` 2. Set DATABASE_URL using the internal database service name and change the initial administrator password immediately. Set DATABASE_URL using the internal database service name and change the initial administrator password immediately. Files: `OPERATIONS.md` 3. Exercise the acceptance fixture Use A user-owned test site with two page loads and one signup-click custom event. Check: The dashboard opens locally and the original default password no longer works. Then check: Restarting both containers preserves the administrator and registered website. Record commands, observed results and the date in OPERATIONS.md. Files: `OPERATIONS.md` ### Done when - [ ] The dashboard opens locally and the original default password no longer works. - [ ] Restarting both containers preserves the administrator and registered website. ### Watch out - Do not implement a second analytics engine or edit upstream database tables. ## Phase 3 · Prove collection Prove collection for Umami Cloud: Register the owned test website and use the exact tracking snippet generated by this installed version. ### Steps 1. Register the owned test website and use the exact tracking snippet generated by this installed version. Register the owned test website and use the exact tracking snippet generated by this installed version. Files: `compose.yaml`, `OPERATIONS.md` 2. Generate the fixture pageviews and named event; inspect the network response and dashboard before declaring collection healthy. Generate the fixture pageviews and named event; inspect the network response and dashboard before declaring collection healthy. Files: `OPERATIONS.md` 3. Exercise the acceptance fixture Use A user-owned test site with two page loads and one signup-click custom event. Check: The named signup-click event appears for the correct website. Then check: No event is attributed to a second website id in the fixture. Record commands, observed results and the date in OPERATIONS.md. Files: `OPERATIONS.md` ### Done when - [ ] The named signup-click event appears for the correct website. - [ ] No event is attributed to a second website id in the fixture. ### Watch out - Collection blocked by consent settings or a browser extension is not evidence that the server is down. ## Phase 4 · Restore and upgrade drill Restore and upgrade drill for Umami Cloud: Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. ### Steps 1. Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. Files: `compose.yaml`, `OPERATIONS.md` 2. Restore into a separate Compose project with a new volume, compare websites/events and rehearse an upgrade there before production. Restore into a separate Compose project with a new volume, compare websites/events and rehearse an upgrade there before production. Files: `OPERATIONS.md` 3. Exercise the acceptance fixture Use A user-owned test site with two page loads and one signup-click custom event. Check: The restored dashboard contains the test website and proof event. Then check: The restore exercise touches no live database volume and records its date. Record commands, observed results and the date in OPERATIONS.md. Files: `OPERATIONS.md` ### Done when - [ ] The restored dashboard contains the test website and proof event. - [ ] The restore exercise touches no live database volume and records its date. ### Watch out - Changing a database major version is a migration, not a routine image pull. ## Phase 5 · Portable delivery and clean-start proof Finish the Umami Cloud replacement with a documented first-run path and a recoverable export. ### Steps 1. Write the user-facing runbook Document prerequisites, every configuration variable, data locations and the three domain workflows: Isolated upstream deployment; Prove collection; Restore and upgrade drill. Include the exclusions, actual costs and source check date 2026-09-05. Commands shown earlier that reference project files are files the builder must implement before running them. Files: `README.md`, `.env.example` 2. Create and rehearse a recoverable backup Use a PostgreSQL logical dump plus the Compose file, image manifest and securely stored deployment secrets. Restore into a separate empty directory or isolated deployment, with all outbound actions and schedules disabled. Recreate this proof: A user-owned test site with two page loads and one signup-click custom event. Compare ids/counts and open the result before recording the restore date. Never test a restore over the only live copy. Files: `OPERATIONS.md`, `backup/README.md` 3. Reproduce the first-run experience Use a new folder and only README instructions to reach the fixture outcome. Verify upstream login, persistence, proof output and restore. Record exactly what passed; do not mark unrun checks complete. Files: `README.md`, `verification.md` ```sh docker compose ps ``` ### Done when - [ ] A new folder reaches the documented fixture outcome using only README instructions. - [ ] A backup restored in isolation preserves the proof data and can be opened. ### Watch out - Export without an import or restore rehearsal is not verified portability. ## Not in this build - Changing a database major version is a migration, not a routine image pull. - This is a user-operated Umami installation with verified collection and restoration; full Umami Cloud feature parity, vendor hosting and support are not included. - managed hosting - upgrades - reliability - team/billing convenience - support ## After v1, if you want it - Extend restore and upgrade drill only after the fixture and recovery checks pass. - Before adding another integration, document its credentials, limits, cost and failure behavior with primary-source links.
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Copy the exact application image reference selected from upstream into compose.yaml via this variable. APP_IMAGE=replace-with-tested-image-and-digest # Required. The loopback URL selected in compose.yaml; replace with your HTTPS origin only after completing product deployment. SITE_URL=http://localhost:3000 # Required · secret. Generate a random password, map it into the database service and URL; keep it outside version control. POSTGRES_PASSWORD=replace-with-random-db-password # Required · secret. Compose internal PostgreSQL connection URL; URL-encode special password characters. DATABASE_URL=postgresql://umami:replace-password@db:5432/umami
# Umami Cloud · product brief ## Problem The software is open source, so the cloud subscription is mostly paying for managed hosting, upgrades, and not touching a server. ## Product outcome An operated version of a user-operated Umami installation with verified collection and restoration, with authorized access, restore evidence and honest scope limits. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - VPS/Docker - database - domain/SSL - backups - update routine ## Explicit non-goals for v1 - Changing a database major version is a migration, not a routine image pull. - This is a user-operated Umami installation with verified collection and restoration; full Umami Cloud feature parity, vendor hosting and support are not included. - managed hosting - upgrades - reliability - team/billing convenience - support ## Success criteria - The dashboard opens locally and the original default password no longer works. - The named signup-click event appears for the correct website. - The restored dashboard contains the test website and proof event. - A private-data authorization check passes using a second identity. - An off-host restore drill has a date, observed result and recovery time in OPERATIONS.md. - README names the excluded vendor capabilities and actual provider/hosting costs.
# Build brief · Umami Cloud The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a user-operated Umami installation with verified collection and restoration as a bounded replacement for Umami Cloud. Build it in phases in the order below. Do not write the whole product in one pass. Finish the current phase, run its Done when checks, fix failures, then continue. Indie mode completes Phases 1-5. Phase 6 is required before shared or public deployment. ### Stack (fixed, do not substitute) - Application: Upstream Umami container. Reuse the maintained application; the work is deployment and operations. - Storage: PostgreSQL. Use the upstream schema and keep data across container replacement. - Deployment: Docker Compose; Caddy for product HTTPS. Explicit volumes, ports and configuration can be reviewed and recreated. ### Data model (create this before Phase 1) - Use the upstream Umami PostgreSQL schema without custom tables. Persist the database, Compose configuration, selected image digest and encrypted deployment secrets. Track a test website id and a named proof event in OPERATIONS.md. - Use stable ids and explicit relationships; add auxiliary tables named by the phases before those phases use them. - Acceptance fixture: A user-owned test site with two page loads and one signup-click custom event. ### Prerequisites and configuration - Docker Engine and Compose v2: Install Docker Engine and the Compose plugin on Linux using the official instructions. On Windows use Docker Desktop with WSL2, then work inside a Linux project folder. Verify: docker compose version prints a v2 version and docker info reaches the daemon. Cost: Docker Engine has no hosting fee; Desktop licensing depends on your use, and a rented host is billed separately. - Choose and record the upstream release: Open https://docs.umami.is/docs/install, choose a stable release compatible with its database requirements, record its tag and resolved digest in versions.txt, and use those values in compose.yaml. Avoid unattended latest-tag updates. Verify: versions.txt names both application and database versions or states that the pilot uses embedded SQLite. Cost: Software/edition terms and optional paid features are defined by the upstream project. - Umami Cloud acceptance sample: Create synthetic sample data before implementation: A user-owned test site with two page loads and one signup-click custom event. Save it under fixtures/ with the expected results in fixtures/README.md; use no real credentials or customer records. Verify: The fixture files and expected outcomes exist and can be inspected without a vendor account. Cost: No paid service required for this local step. - Confirm the bounded replacement: The deliverable is a user-operated Umami installation with verified collection and restoration. Read every exclusion below. Indie mode runs locally; any public or shared use requires the product access and deployment gates first. Verify: You can name the primary workflow and the vendor features this version deliberately leaves out. Cost: No paid service required for this local step. - Configuration: APP_IMAGE, SITE_URL, POSTGRES_PASSWORD, DATABASE_URL. Ship .env.example, replace placeholders and exclude secrets from git. ### Phase 1 · Deployment inventory and safe pilot Build: Prepare the pinned upstream deployment for a user-operated Umami installation with verified collection and restoration. - Read the linked upstream installation guide and create compose.yaml, .env.example, versions.txt and OPERATIONS.md. Map APP_IMAGE and only each service's required environment variables explicitly. Keep database ports internal and map the pilot application port to 127.0.0.1. - Use the upstream Umami PostgreSQL schema without custom tables. Persist the database, Compose configuration, selected image digest and encrypted deployment secrets. Track a test website id and a named proof event in OPERATIONS.md. Name each volume and its mount path in OPERATIONS.md; never use ephemeral container storage for the only copy. Create .env manually from .env.example and replace placeholders before starting. Commands, after creating the named files: `docker compose config --quiet`. - Create a private fixtures/ folder containing: A user-owned test site with two page loads and one signup-click custom event. Create a separate backup destination and an isolated restore-project name; do not reuse live volumes in restore exercises. - Trap: A valid Compose file proves configuration syntax only, not application health. Done when: docker compose config --quiet succeeds with no unresolved required variables. The documented volume list includes every persistent asset and no secret is committed. Do not build yet: Do not start isolated upstream deployment until these checks pass. ### Phase 2 · Isolated upstream deployment Build: Isolated upstream deployment for Umami Cloud: Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. - Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. - Set DATABASE_URL using the internal database service name and change the initial administrator password immediately. - Use A user-owned test site with two page loads and one signup-click custom event. Check: The dashboard opens locally and the original default password no longer works. Then check: Restarting both containers preserves the administrator and registered website. Record commands, observed results and the date in OPERATIONS.md. - Trap: Do not implement a second analytics engine or edit upstream database tables. Done when: The dashboard opens locally and the original default password no longer works. Restarting both containers preserves the administrator and registered website. Do not build yet: Do not start prove collection until these checks pass. ### Phase 3 · Prove collection Build: Prove collection for Umami Cloud: Register the owned test website and use the exact tracking snippet generated by this installed version. - Register the owned test website and use the exact tracking snippet generated by this installed version. - Generate the fixture pageviews and named event; inspect the network response and dashboard before declaring collection healthy. - Use A user-owned test site with two page loads and one signup-click custom event. Check: The named signup-click event appears for the correct website. Then check: No event is attributed to a second website id in the fixture. Record commands, observed results and the date in OPERATIONS.md. - Trap: Collection blocked by consent settings or a browser extension is not evidence that the server is down. Done when: The named signup-click event appears for the correct website. No event is attributed to a second website id in the fixture. Do not build yet: Do not start restore and upgrade drill until these checks pass. ### Phase 4 · Restore and upgrade drill Build: Restore and upgrade drill for Umami Cloud: Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. - Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. - Restore into a separate Compose project with a new volume, compare websites/events and rehearse an upgrade there before production. - Use A user-owned test site with two page loads and one signup-click custom event. Check: The restored dashboard contains the test website and proof event. Then check: The restore exercise touches no live database volume and records its date. Record commands, observed results and the date in OPERATIONS.md. - Trap: Changing a database major version is a migration, not a routine image pull. Done when: The restored dashboard contains the test website and proof event. The restore exercise touches no live database volume and records its date. Do not build yet: Do not start portable delivery and clean-start proof until these checks pass. ### Phase 5 · Portable delivery and clean-start proof Build: Finish the Umami Cloud replacement with a documented first-run path and a recoverable export. - Document prerequisites, every configuration variable, data locations and the three domain workflows: Isolated upstream deployment; Prove collection; Restore and upgrade drill. Include the exclusions, actual costs and source check date 2026-09-05. Commands shown earlier that reference project files are files the builder must implement before running them. - Use a PostgreSQL logical dump plus the Compose file, image manifest and securely stored deployment secrets. Restore into a separate empty directory or isolated deployment, with all outbound actions and schedules disabled. Recreate this proof: A user-owned test site with two page loads and one signup-click custom event. Compare ids/counts and open the result before recording the restore date. Never test a restore over the only live copy. - Use a new folder and only README instructions to reach the fixture outcome. Verify upstream login, persistence, proof output and restore. Record exactly what passed; do not mark unrun checks complete. Commands, after creating the named files: `docker compose ps`. - Trap: Export without an import or restore rehearsal is not verified portability. Done when: A new folder reaches the documented fixture outcome using only README instructions. A backup restored in isolation preserves the proof data and can be opened. Do not build yet: Do not start operate and share with access controls until these checks pass. ### Phase 6 · Operate and share with access controls (product-builder only) Build: Product-builder only: make a user-operated Umami installation with verified collection and restoration operable for its stated audience before allowing remote access. - Use upstream authentication, replace default credentials, restrict the editor/admin surface and inventory public ingestion/webhook paths. Review upstream edition and license boundaries before offering this as a service. - Put Caddy HTTPS in front of the upstream app, keep database and management ports private and configure public URLs from the chosen domain. For n8n, rehearse upstream PostgreSQL migration/import and recovery before claiming production readiness; disable any unused AI sandbox services. - Add a scheduled encrypted off-host backup using this procedure: Use a PostgreSQL logical dump plus the Compose file, image manifest and securely stored deployment secrets. Monitor health, disk space, failed jobs and backup age. Stop the service to test an alert; rehearse recovery with outbound effects disabled and record recovery time. Rotate any exposed credentials and require reconciliation before resuming queues. - Trap: Product mode is additional engineering, not a switch that makes a local prototype production-ready. Done when: An unauthenticated visitor cannot read private records, download private files or mutate the admin surface. A viewer cannot mutate data by making a direct request, and guessed ids cannot cross ownership boundaries. Stopping the service triggers the configured alert and the latest off-host backup restores the fixture in isolation. Do not build yet: Do not add new providers or vendor-scale features until the release gates are recorded. ### Out of scope (and why) - Changing a database major version is a migration, not a routine image pull. - This is a user-operated Umami installation with verified collection and restoration; full Umami Cloud feature parity, vendor hosting and support are not included. - managed hosting: retain the paid product if this capability is essential; it is outside this bounded build. - upgrades: retain the paid product if this capability is essential; it is outside this bounded build. - reliability: retain the paid product if this capability is essential; it is outside this bounded build. - team/billing convenience: retain the paid product if this capability is essential; it is outside this bounded build. - support: retain the paid product if this capability is essential; it is outside this bounded build. ### README must contain - Exact install, configure, initialize, start, test and recovery commands for a clean folder. - The schema, fixture, acceptance results, private data locations and export format. - Every prerequisite and secret source, actual running costs and deliberately excluded capabilities. - An honest distinction between tests actually run and checks still awaiting real credentials or deployment. - Dependency documentation reviewed 2026-09-05: https://docs.umami.is/docs/install ; https://www.postgresql.org/docs/current/backup-dump.html. Recheck before upgrading; freeze the versions actually tested.
# Architecture · Umami Cloud ## Stack | Part | Choice | Why | | --- | --- | --- | | Application | Upstream Umami container | Reuse the maintained application; the work is deployment and operations. | | Storage | PostgreSQL | Use the upstream schema and keep data across container replacement. | | Deployment | Docker Compose; Caddy for product HTTPS | Explicit volumes, ports and configuration can be reviewed and recreated. | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Data and identity | Use the upstream Umami PostgreSQL schema without custom tables. Persist the database, Compose configuration, selected image digest and encrypted deployment secrets. Track a test website id and a named proof event in OPERATIONS.md. | Follow upstream migration procedures and verify a restored fixture before switching stores. | | Isolated upstream deployment | Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. | Keep the acceptance contract: The dashboard opens locally and the original default password no longer works. Replace only this module after replaying its fixture; preserve ids and existing records. | | Prove collection | Register the owned test website and use the exact tracking snippet generated by this installed version. | Keep the acceptance contract: The named signup-click event appears for the correct website. Replace only this module after replaying its fixture; preserve ids and existing records. | | Restore and upgrade drill | Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. | Keep the acceptance contract: The restored dashboard contains the test website and proof event. Replace only this module after replaying its fixture; preserve ids and existing records. | | Operations | Use a PostgreSQL logical dump plus the Compose file, image manifest and securely stored deployment secrets. | Change host or backup destination only after an isolated restore and authorization check. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `APP_IMAGE` · required · Copy the exact application image reference selected from upstream into compose.yaml via this variable. - `SITE_URL` · required · The loopback URL selected in compose.yaml; replace with your HTTPS origin only after completing product deployment. - `POSTGRES_PASSWORD` · required, secret · Generate a random password, map it into the database service and URL; keep it outside version control. - `DATABASE_URL` · required, secret · Compose internal PostgreSQL connection URL; URL-encode special password characters. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it.
# Agent instructions · Umami Cloud product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Upstream Umami container, PostgreSQL, Docker Compose; Caddy for product HTTPS. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - A valid Compose file proves configuration syntax only, not application health. - Do not implement a second analytics engine or edit upstream database tables. - Collection blocked by consent settings or a browser extension is not evidence that the server is down. - Changing a database major version is a migration, not a routine image pull. - Export without an import or restore rehearsal is not verified portability. - Product mode is additional engineering, not a switch that makes a local prototype production-ready.
# Delivery milestones · Umami Cloud Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Deployment inventory and safe pilot Prepare the pinned upstream deployment for a user-operated Umami installation with verified collection and restoration. ### Steps 1. Create the deployment files Read the linked upstream installation guide and create compose.yaml, .env.example, versions.txt and OPERATIONS.md. Map APP_IMAGE and only each service's required environment variables explicitly. Keep database ports internal and map the pilot application port to 127.0.0.1. Files: `compose.yaml`, `.env.example`, `versions.txt` 2. Identify every persistent asset Use the upstream Umami PostgreSQL schema without custom tables. Persist the database, Compose configuration, selected image digest and encrypted deployment secrets. Track a test website id and a named proof event in OPERATIONS.md. Name each volume and its mount path in OPERATIONS.md; never use ephemeral container storage for the only copy. Create .env manually from .env.example and replace placeholders before starting. Files: `OPERATIONS.md`, `.gitignore` ```sh docker compose config --quiet ``` 3. Prepare the proof and recovery folder Create a private fixtures/ folder containing: A user-owned test site with two page loads and one signup-click custom event. Create a separate backup destination and an isolated restore-project name; do not reuse live volumes in restore exercises. Files: `fixtures/README.md`, `OPERATIONS.md` ### Done when - [ ] docker compose config --quiet succeeds with no unresolved required variables. - [ ] The documented volume list includes every persistent asset and no secret is committed. ### Watch out - A valid Compose file proves configuration syntax only, not application health. ## M2 · Isolated upstream deployment Isolated upstream deployment for Umami Cloud: Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. ### Steps 1. Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. Deploy the official Umami container with PostgreSQL on an internal Compose network and a loopback-only app port; pin tested image digests. Files: `compose.yaml`, `OPERATIONS.md` 2. Set DATABASE_URL using the internal database service name and change the initial administrator password immediately. Set DATABASE_URL using the internal database service name and change the initial administrator password immediately. Files: `OPERATIONS.md` 3. Exercise the acceptance fixture Use A user-owned test site with two page loads and one signup-click custom event. Check: The dashboard opens locally and the original default password no longer works. Then check: Restarting both containers preserves the administrator and registered website. Record commands, observed results and the date in OPERATIONS.md. Files: `OPERATIONS.md` ### Done when - [ ] The dashboard opens locally and the original default password no longer works. - [ ] Restarting both containers preserves the administrator and registered website. ### Watch out - Do not implement a second analytics engine or edit upstream database tables. ## M3 · Prove collection Prove collection for Umami Cloud: Register the owned test website and use the exact tracking snippet generated by this installed version. ### Steps 1. Register the owned test website and use the exact tracking snippet generated by this installed version. Register the owned test website and use the exact tracking snippet generated by this installed version. Files: `compose.yaml`, `OPERATIONS.md` 2. Generate the fixture pageviews and named event; inspect the network response and dashboard before declaring collection healthy. Generate the fixture pageviews and named event; inspect the network response and dashboard before declaring collection healthy. Files: `OPERATIONS.md` 3. Exercise the acceptance fixture Use A user-owned test site with two page loads and one signup-click custom event. Check: The named signup-click event appears for the correct website. Then check: No event is attributed to a second website id in the fixture. Record commands, observed results and the date in OPERATIONS.md. Files: `OPERATIONS.md` ### Done when - [ ] The named signup-click event appears for the correct website. - [ ] No event is attributed to a second website id in the fixture. ### Watch out - Collection blocked by consent settings or a browser extension is not evidence that the server is down. ## M4 · Restore and upgrade drill Restore and upgrade drill for Umami Cloud: Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. ### Steps 1. Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. Create a consistent PostgreSQL logical backup and record the matching app/database versions and secret recovery location. Files: `compose.yaml`, `OPERATIONS.md` 2. Restore into a separate Compose project with a new volume, compare websites/events and rehearse an upgrade there before production. Restore into a separate Compose project with a new volume, compare websites/events and rehearse an upgrade there before production. Files: `OPERATIONS.md` 3. Exercise the acceptance fixture Use A user-owned test site with two page loads and one signup-click custom event. Check: The restored dashboard contains the test website and proof event. Then check: The restore exercise touches no live database volume and records its date. Record commands, observed results and the date in OPERATIONS.md. Files: `OPERATIONS.md` ### Done when - [ ] The restored dashboard contains the test website and proof event. - [ ] The restore exercise touches no live database volume and records its date. ### Watch out - Changing a database major version is a migration, not a routine image pull. ## M5 · Portable delivery and clean-start proof Finish the Umami Cloud replacement with a documented first-run path and a recoverable export. ### Steps 1. Write the user-facing runbook Document prerequisites, every configuration variable, data locations and the three domain workflows: Isolated upstream deployment; Prove collection; Restore and upgrade drill. Include the exclusions, actual costs and source check date 2026-09-05. Commands shown earlier that reference project files are files the builder must implement before running them. Files: `README.md`, `.env.example` 2. Create and rehearse a recoverable backup Use a PostgreSQL logical dump plus the Compose file, image manifest and securely stored deployment secrets. Restore into a separate empty directory or isolated deployment, with all outbound actions and schedules disabled. Recreate this proof: A user-owned test site with two page loads and one signup-click custom event. Compare ids/counts and open the result before recording the restore date. Never test a restore over the only live copy. Files: `OPERATIONS.md`, `backup/README.md` 3. Reproduce the first-run experience Use a new folder and only README instructions to reach the fixture outcome. Verify upstream login, persistence, proof output and restore. Record exactly what passed; do not mark unrun checks complete. Files: `README.md`, `verification.md` ```sh docker compose ps ``` ### Done when - [ ] A new folder reaches the documented fixture outcome using only README instructions. - [ ] A backup restored in isolation preserves the proof data and can be opened. ### Watch out - Export without an import or restore rehearsal is not verified portability. ## M6 · Operate and share with access controls (production only) Product-builder only: make a user-operated Umami installation with verified collection and restoration operable for its stated audience before allowing remote access. ### Steps 1. Establish owner access before exposure Use upstream authentication, replace default credentials, restrict the editor/admin surface and inventory public ingestion/webhook paths. Review upstream edition and license boundaries before offering this as a service. Files: `ARCHITECTURE.md`, `OPERATIONS.md` 2. Deploy a monitored service Put Caddy HTTPS in front of the upstream app, keep database and management ports private and configure public URLs from the chosen domain. For n8n, rehearse upstream PostgreSQL migration/import and recovery before claiming production readiness; disable any unused AI sandbox services. Files: `Caddyfile`, `OPERATIONS.md` 3. Rehearse loss and recovery Add a scheduled encrypted off-host backup using this procedure: Use a PostgreSQL logical dump plus the Compose file, image manifest and securely stored deployment secrets. Monitor health, disk space, failed jobs and backup age. Stop the service to test an alert; rehearse recovery with outbound effects disabled and record recovery time. Rotate any exposed credentials and require reconciliation before resuming queues. Files: `OPERATIONS.md`, `release-checklist.md` ### Done when - [ ] An unauthenticated visitor cannot read private records, download private files or mutate the admin surface. - [ ] A viewer cannot mutate data by making a direct request, and guessed ids cannot cross ownership boundaries. - [ ] Stopping the service triggers the configured alert and the latest off-host backup restores the fixture in isolation. ### Watch out - Product mode is additional engineering, not a switch that makes a local prototype production-ready.
# Operations · Umami Cloud ## Backup Use a PostgreSQL logical dump plus the Compose file, image manifest and securely stored deployment secrets. ## Restore Restore into a separate empty directory or isolated deployment, with all outbound actions and schedules disabled. Recreate this proof: A user-owned test site with two page loads and one signup-click custom event. Compare ids/counts and open the result before recording the restore date. Never test a restore over the only live copy. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Check HTTP availability, free disk, backup age and domain failures for Isolated upstream deployment, Prove collection, Restore and upgrade drill. Alert without logging private payloads. ## Incident checklist Pause outbound jobs and public intake as appropriate, preserve redacted diagnostics, restore an isolated verified backup, rotate affected credentials and reconcile uncertain effects before reopening. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] The dashboard opens locally and the original default password no longer works. - [ ] The named signup-click event appears for the correct website. - [ ] The restored dashboard contains the test website and proof event. - [ ] A private-data authorization check passes using a second identity. - [ ] An off-host restore drill has a date, observed result and recovery time in OPERATIONS.md. - [ ] README names the excluded vendor capabilities and actual provider/hosting costs. ## Launch constraint Do not market omitted Umami Cloud capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered.
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Copy the exact application image reference selected from upstream into compose.yaml via this variable. APP_IMAGE=replace-with-tested-image-and-digest # Required. The loopback URL selected in compose.yaml; replace with your HTTPS origin only after completing product deployment. SITE_URL=http://localhost:3000 # Required · secret. Generate a random password, map it into the database service and URL; keep it outside version control. POSTGRES_PASSWORD=replace-with-random-db-password # Required · secret. Compose internal PostgreSQL connection URL; URL-encode special password characters. DATABASE_URL=postgresql://umami:replace-password@db:5432/umami
$ choose a build depth, inspect the files, then open the complete pack in your agent
They pay because analytics is not worth waking up to fix.
xmanaged hosting
xupgrades
xreliability
xteam/billing convenience
xsupport
Don't feel like building it? These folks already made it free.
all 4 free alternatives to Umami Cloud →· no votes, no pay-to-list · just what's real
Umami Cloud pricing
pro$20/mo · monthly · $240/yr
free tierThe free Hobby plan covers 100k events a month on 3 sites with 6 months retention.
verified 2026-08-07 · source ↗
Is Umami Cloud free?
The free Hobby plan covers 100k events a month on 3 sites with 6 months retention. Paid is Pro at $20/mo (checked 2026-08-07).
Vibecode Umami Cloud
Yes. A competent AI coding agent (Claude Code, Codex, Cursor) can build a usable personal Umami Cloud replacement in one session with the prompt on this page. It runs on your own machine or server with no subscription.
How much does Umami Cloud cost?
Umami Cloud costs about $20/month (Pro, checked 2026-08-07), which is $240 per year. That's what you save by replacing it with one prompt.
What do I lose by replacing Umami Cloud?
Honestly: managed hosting; upgrades; reliability; team/billing convenience; support. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to Umami Cloud?
Yes: Umami (Exactly Umami, except the cloud engineer is now you and Docker gets a vote.) Plausible Community Edition (A similarly restrained privacy dashboard with a mature self-hosted edition.) Rybbit (A broader self-hosted alternative with replay and funnels, at the cost of more moving parts.) All 4 curated free alternatives are at vibecodeit.com/umami-cloud/alternatives. The prompt is for when you want it exactly your way.