Plausible Analytics
Audited from github.com/plausible/analytics
Plausible Analytics is an open-source, privacy-first web analytics platform that provides cookie-free tracking using Elixir, Phoenix, ClickHouse, and PostgreSQL.
- Language
- Elixir
- Database
- PostgreSQL
- Hosting
- Docker
- License
- AGPL-3.0
- Running for
- 5 years 2 months
- Team
- 2-5 people
Why this architecture
Pairing the BEAM virtual machine with ClickHouse allows Plausible to ingest high-volume telemetry surges reliably with minimal RAM and fast analytical queries.
Tech stack by layer
8 technologies · audited Sep 17, 2026- ReactPowers the analytics dashboards, stats exploration, and settings UI in assets/package.json.
- Tailwind CSSProvides utility-first styling for the dashboard with @tailwindcss/forms and typography plugins.
- TanStack QueryHandles asynchronous data fetching and cache management across the React analytics views.
- D3Renders custom SVG visualizations and geographical choropleth maps alongside topojson-client.
- ElixirCore functional programming language driving the BEAM virtual machine concurrency layer.
- PhoenixPhoenix 1.8 web framework serving HTTP endpoints, LiveView pages, and WebSocket channels.
- ObanPostgreSQL-backed background job processing for scheduled digests, imports, and data cleanup.
- OpenTelemetryInstruments distributed tracing across Phoenix requests, Ecto queries, and Oban job executions.
- PostgreSQLStores relational entities including users, sites, subscription plans, and API keys via Ecto.
- ClickHouseColumnar analytical database storing high-volume pageview events and aggregated visitor sessions.
- S3Object storage for import and export datasets integrated via ex_aws and ex_aws_s3.
- DockerMulti-stage Dockerfile packaging the compiled Elixir release on Alpine Linux for production.
- PlaywrightEnd-to-end browser test automation under e2e/ covering full dashboard interactions.
Plausible Analytics architecture diagram
Open SVGDiagram as text
- Tracked sites (tracker (Rollup)) → Phoenix app (Elixir · Phoenix 1.8): event beacon
- Dashboard (React · TanStack Query) → Phoenix app (Elixir · Phoenix 1.8): stats API
- Phoenix app (Elixir · Phoenix 1.8) → ClickHouse (events · sessions): ingest · query
- Phoenix app (Elixir · Phoenix 1.8) → PostgreSQL (sites · users (Ecto)): Ecto
- Oban jobs (digests · imports · cleanup) → PostgreSQL (sites · users (Ecto)): job queue
- Oban jobs (digests · imports · cleanup) → S3 (import / export): exports
Key architectural decisions
5 decisions- 01
Split storage: PostgreSQL for configuration, ClickHouse for high-throughput events
Ecto connects to PostgreSQL via postgrex for transactional data (accounts, sites, memberships), while ecto_ch routes analytical write streams and aggregated queries to a columnar ClickHouse cluster.
- 02
Standalone tracking script isolated from AGPL application code
The lightweight client script lives in tracker/ with an independent MIT license and Rollup/SWC build toolchain, preventing AGPL copyleft viral obligations on websites embedding the snippet.
- 03
Hybrid Elixir release packaging with Docker multi-stage compilation
The root Dockerfile compiles frontend assets using Node 24 and assembles a self-contained Elixir OTP release on Alpine 3.23, eliminating development runtimes from the final container.
- 04
PostgreSQL-backed background workers via Oban instead of Redis queues
Background tasks, report generation, and data retention jobs run through Oban directly in PostgreSQL, eliminating Redis from the core infrastructure dependencies.
- 05
Client-side SPA dashboard embedded directly within Phoenix views
The main dashboard in assets/ is a TypeScript React single-page application bundled with esbuild and served from Phoenix templates with digested static assets.
How Plausible Analytics is built
How Plausible Analytics is structured
Plausible Analytics is structured as an OTP release application written in Elixir (lib/plausible.ex, lib/plausible_web.ex), accompanied by decoupled frontend and tracking client subprojects. The root directory coordinates application configuration (config/config.exs, config/runtime.exs), database schemas and migrations (priv/repo, priv/ingest_repo), background job workers (lib/workers), and the HTTP layer (lib/plausible_web).
Client-side application assets reside in assets/, where a TypeScript and React single-page dashboard is maintained with its own package.json, ESLint, and Jest test configuration. In addition, the tracking snippet that client websites embed is completely segregated in tracker/. This subdirectory contains its own build pipeline (tracker/compile.js, tracker/package.json) using Rollup and SWC, ensuring that tracking scripts remain compact (under 1 KB) and completely separate from the main backend repository lifecycle.
The application uses Elixir's native release mechanism (rel/vm.args.eex, rel/docker-entrypoint.sh), creating an immutable runtime artifact that runs inside an Alpine Linux container without requiring Elixir, Erlang, or Node.js runtimes in the final deployment image.
Frontend
The user-facing dashboard is located in assets/ and is built using React 18 (react, react-dom in assets/package.json). Rather than relying on heavyweight UI frameworks, the interface leverages Tailwind CSS (@tailwindcss/forms, @tailwindcss/typography) and Headless UI primitives (@headlessui/react, @heroicons/react).
Data visualization is central to Plausible's analytics interface. The application relies on D3 (d3 version 7.9) and TopoJSON (topojson-client, visionscarto-world-atlas) to render interactive geographic maps, visitor distribution graphics, and trend sparklines. Asynchronous data synchronization and query state management are driven by TanStack Query (@tanstack/react-query). The build pipeline uses Phoenix's asset pipeline with esbuild and tailwind runners declared in mix.exs aliases (assets.setup, assets.build, assets.deploy), minifying bundles and generating fingerprint digests in priv/static.
Backend & APIs
The backend is powered by Elixir on the Erlang BEAM virtual machine, using the Phoenix 1.8 framework (phoenix, phoenix_html, phoenix_live_view in mix.exs). Phoenix controllers and plugs handle incoming tracking beacon ingestion, administrative REST endpoints, user authentication, and public sharing routes.
For background task execution and job scheduling, Plausible integrates Oban (oban 2.24). Oban relies entirely on PostgreSQL advisory locks and transactional tables rather than Redis, executing scheduled tasks such as automated weekly email digests, traffic spike alerts, and data purging jobs. Observability is integrated natively using OpenTelemetry (opentelemetry, opentelemetry_phoenix, opentelemetry_ecto, opentelemetry_oban), exporting traces and metrics to OpenTelemetry collectors.
Data & persistence
Plausible implements a dual-database architecture:
- PostgreSQL: Managed via Ecto (
ecto,ecto_sql,postgrex). PostgreSQL serves as the relational source of truth for accounts, user memberships, site registrations, custom event goal definitions, weekly email subscriptions, and API keys. - ClickHouse: Analytical events and session aggregation are routed to ClickHouse through
ecto_ch. ClickHouse stores events in columnar format, enabling high-speed aggregation over billions of rows without loading entire datasets into memory.
For transient storage, export dumps, and imports from Google Analytics, Plausible connects to S3-compatible object stores via ex_aws and ex_aws_s3. The local development environment defined in Makefile provisions a MinIO instance (plausible_minio) to simulate S3 bucket operations locally.
Build, test & deploy
The build lifecycle is governed by Mix and Docker:
- Testing: Elixir unit and integration tests run via
mix testusing ExUnit, Mox for HTTP mocks, and Bypass. Frontend tests run Jest (assets/package.json). Full end-to-end user journeys are verified using Playwright ine2e/playwright.config.ts, testing Chromium browser interactions against running test database containers. - Continuous Integration: GitHub Actions workflows in
.github/workflowsexecuteelixir.ymlfor Mix compilation, Credo formatting, Dialyzer type checking, and unit testing, whilenode.ymlverifies asset builds and linting. - Docker Production Image: The multi-stage
Dockerfileusesnode:24-alpineto compile assets,hexpm/elixir:1.20-erlang-28-alpineto compile the Elixir OTP release, and produces a minimal final image based onalpine:3.23.
Self-hosting notes
Plausible provides a Community Edition (CE) deployment model designed to run on standard container platforms. As outlined in the Makefile and Dockerfile, self-hosters must operate:
- An Elixir application container (
plausible). - A PostgreSQL database instance (version 16 or newer) for core application data.
- A ClickHouse server (
clickhouse/clickhouse-server) for analytics storage. - An optional SMTP or Postmark mail relay for invitation and password reset emails.
The release scripts under rel/ handle environment variable parsing at boot time via config/runtime.exs, allowing configuration of domain names, ports, and secret keys without rebuilding the container.
What to copy (and what not to)
Plausible's clean separation of concerns provides architectural patterns well worth adopting:
- Separation of metadata and analytics storage: Storing users and configuration in PostgreSQL while delegating analytical event streams to ClickHouse provides high write throughput and analytical query speeds without overwhelming transactional relational tables.
- Licensing boundaries for embedded assets: Isolating the tracking client in
tracker/under an MIT license while retaining AGPL-3.0 for the server core allows open-source web applications to offer embeddable snippets without causing legal friction for end users. - Single-dependency background processing: Adopting Oban for background queues keeps infrastructure simpler by leveraging PostgreSQL ACID guarantees instead of maintaining a separate Redis cluster.
Conversely, avoid duplicating Plausible's multi-step asset compilation across multiple package managers (mix, npm in assets/, npm in tracker/) unless strict binary isolation between consumer client snippets and admin interfaces is a primary design constraint.
Sources & repo audit
- mix.exs (Elixir dependencies, OTP application setup, releases)
- Dockerfile (Alpine multi-stage build, Node asset compilation, Elixir release)
- assets/package.json (React dashboard, Tailwind CSS, TanStack Query, D3)
- tracker/package.json (Standalone tracker script build with Rollup and SWC)
- Makefile (Local services: ClickHouse, PostgreSQL, MinIO, CoreDNS)
Independent analysis of repository at github.com/plausible/analytics. Spotted an inaccuracy? Use the claim form to request a correction.
Show this stack on your README or website
[](https://stackitfast.com/project/plausible)Paste it into your README. It shows your current stack and links to this page.
Scaffold it with your agent
Paste this prompt into Claude Code, Cursor, Windsurf or AGY to start a project with Plausible Analytics's architecture.
- 1Copy the promptThe full markdown spec, with every layer and decision.
- 2Open your AI toolClaude Code, Cursor, Windsurf or Copilot, in a new repo.
- 3Paste and scaffoldUse it as the first instruction; review before you ship.
# MISSION: Scaffold "Plausible Analytics" Production Architecture
You are an expert Senior Staff Software Architect and Full-Stack Engineer. Your mission is to scaffold and implement a production-grade, highly reliable, and modular codebase following the proven architecture of **Plausible Analytics**.
---
## 1. PROJECT SPECIFICATIONS & BENCHMARK
- **Reference Architecture**: Plausible Analytics
- **What It Does**: Plausible Analytics is an open-source, privacy-first web analytics platform that provides cookie-free tracking using Elixir, Phoenix, ClickHouse, and PostgreSQL.
- **Domain & Category**: Privacy Web Analytics
- **Production Scale**: 2-5 people
- **Development Mode**: CLASSIC
- **Architectural Rationale**: Pairing the BEAM virtual machine with ClickHouse allows Plausible to ingest high-volume telemetry surges reliably with minimal RAM and fast analytical queries.
- **Live Website Reference**: https://plausible.io
- **Source Repository**: https://github.com/plausible/analytics
---
## 2. PRODUCTION TECH STACK
- **Full Stack Array**: Elixir, Phoenix, PostgreSQL, ClickHouse, React, Tailwind CSS, Docker, Playwright
- **Primary Language(s)**: Elixir, TypeScript, JavaScript
- **License of the reference repo**: AGPL-3.0
- **Frontend**: React — Powers the analytics dashboards, stats exploration, and settings UI in assets/package.json.; Tailwind CSS — Provides utility-first styling for the dashboard with @tailwindcss/forms and typography plugins.; TanStack Query — Handles asynchronous data fetching and cache management across the React analytics views.; D3 — Renders custom SVG visualizations and geographical choropleth maps alongside topojson-client.
- **Backend & APIs**: Elixir — Core functional programming language driving the BEAM virtual machine concurrency layer.; Phoenix — Phoenix 1.8 web framework serving HTTP endpoints, LiveView pages, and WebSocket channels.; Oban — PostgreSQL-backed background job processing for scheduled digests, imports, and data cleanup.; OpenTelemetry — Instruments distributed tracing across Phoenix requests, Ecto queries, and Oban job executions.
- **Data & persistence**: PostgreSQL — Stores relational entities including users, sites, subscription plans, and API keys via Ecto.; ClickHouse — Columnar analytical database storing high-volume pageview events and aggregated visitor sessions.; S3 — Object storage for import and export datasets integrated via ex_aws and ex_aws_s3.
- **Infrastructure & deploy**: Docker — Multi-stage Dockerfile packaging the compiled Elixir release on Alpine Linux for production.; Playwright — End-to-end browser test automation under e2e/ covering full dashboard interactions.
---
## 3. KEY ARCHITECTURAL DECISIONS (audited from https://github.com/plausible/analytics @ 30abd27)
1. **Split storage: PostgreSQL for configuration, ClickHouse for high-throughput events**: Ecto connects to PostgreSQL via postgrex for transactional data (accounts, sites, memberships), while ecto_ch routes analytical write streams and aggregated queries to a columnar ClickHouse cluster.
2. **Standalone tracking script isolated from AGPL application code**: The lightweight client script lives in tracker/ with an independent MIT license and Rollup/SWC build toolchain, preventing AGPL copyleft viral obligations on websites embedding the snippet.
3. **Hybrid Elixir release packaging with Docker multi-stage compilation**: The root Dockerfile compiles frontend assets using Node 24 and assembles a self-contained Elixir OTP release on Alpine 3.23, eliminating development runtimes from the final container.
4. **PostgreSQL-backed background workers via Oban instead of Redis queues**: Background tasks, report generation, and data retention jobs run through Oban directly in PostgreSQL, eliminating Redis from the core infrastructure dependencies.
5. **Client-side SPA dashboard embedded directly within Phoenix views**: The main dashboard in assets/ is a TypeScript React single-page application bundled with esbuild and served from Phoenix templates with digested static assets.
---
## 4. NON-NEGOTIABLE ARCHITECTURAL GUARDRAILS
1. **Monorepo & Modular Separation**:
- Structure as a Turborepo monorepo with strict package boundaries:
- `apps/web`: Application UI, routing, layouts, and server endpoints.
- `packages/ui`: Shared design tokens, CSS variables, and Radix UI primitive components.
- `packages/db`: Database schemas, client singleton, declarative migrations, and seed scripts.
- `packages/config`: Shared TypeScript, ESLint, and build configurations.
2. **Strict Type Safety & Zero `any` Policy**:
- Enable `strict: true`, `noImplicitAny: true`, and `strictNullChecks: true`.
- Validate ALL external inputs, API request bodies, and query parameters with **Zod** schemas before execution.
3. **Frontend & Rendering Guidelines**:
- Isolate interactive UI state to leaf components. Keep core pages lightweight and performant.
4. **Design System & Aesthetics**:
- Keep every color, radius, shadow and font in a single token file (CSS variables) and consume tokens everywhere; never hardcode hex values in components.
- Prefer crisp 1px borders and one subtle shadow scale over blurry default shadows. Pair one sans-serif for body/headings with one monospace for tags, badges, metrics, and code.
5. **Data Layer & Reliability**:
- Write declarative schema definitions with foreign keys, composite indexes on queried filters, and automated timestamp triggers.
- Use connection pooling and prepared statements for serverless database execution.
---
## 5. STEP-BY-STEP SCAFFOLDING ROADMAP
- **Phase 1: Workspace & Root Config**: Initialize package manager, monorepo configuration (`turbo.json`, `tsconfig.base.json`, `package.json`).
- **Phase 2: Database Schema & Client**: Set up the data layer (PostgreSQL, ClickHouse, S3): client, connection pool, models, and migration scripts.
- **Phase 3: Design Tokens & UI Primitives**: Build accessible `Button`, `Input`, `Card`, `Badge`, and layout wrappers inside `packages/ui`.
- **Phase 4: Core Application Routes & Handlers**: Implement primary authentication, user session handling, and application routes.
- **Phase 5: Quality Assurance & Build Verification**: Run `tsc --noEmit`, ESLint, Prettier, and smoke test suites to ensure zero compilation or runtime errors.
---
## 6. EXECUTION INSTRUCTIONS
1. Review all specifications, architectural guardrails, and stack choices above.
2. Present the full monorepo directory tree structure.
3. Systematically generate the complete, production-ready codebase according to the 5-phase roadmap above — starting with the root workspace setup, followed by the database schema, UI design system package, and full-stack application routes until the repository is fully scaffolded and ready to run.Frequently asked about Plausible Analytics
What is Plausible Analytics built with?
Plausible is built with Elixir and the Phoenix framework on the BEAM VM, PostgreSQL for metadata, ClickHouse for columnar analytics storage, and a React frontend.
Why does Plausible use ClickHouse alongside PostgreSQL?
PostgreSQL handles transactional entities like accounts, sites, and billing, while ClickHouse stores and queries millions of analytical pageviews with sub-second columnar aggregation.
Is the tracking script subject to the AGPL license?
No. The client-side tracker is authored under tracker/ and published to npm under the MIT license, preventing copyleft contamination for websites embedding the snippet.
Can Plausible be self-hosted with Docker?
Yes. Plausible ships a production Dockerfile and a Community Edition configuration that runs with PostgreSQL and ClickHouse containers via Docker Compose.
One email a month: new deep dives and stack trends
New source-audited architectures, head-to-head comparisons and the monthly stack report. No spam, unsubscribe anytime.
Similar architectures
- UmamiHybridWeb Analytics · SoloShares React · PostgreSQL · ClickHouse
- PostHogHybridProduct Analytics Platform · 20+ peopleShares React · ClickHouse · PostgreSQL
- SentryClassicError Tracking Platform · 20+ peopleShares React · ClickHouse · PostgreSQL
- GrafanaClassicMonitoring & Observability Dashboard · 20+ peopleShares React · PostgreSQL · Docker
- InfisicalHybridSecret Management · 6-20 peopleShares React · PostgreSQL · ClickHouse
- TwentyHybridCRM SaaS · 6-20 peopleShares React · PostgreSQL · ClickHouse