Lago is an open-source metering and usage-based billing platform: a Ruby on Rails API and React dashboard (versioned as git submodules) handle subscriptions and invoicing, while a separate Go service and Kafka-based connectors ingest and process high-volume usage events, sharing a Rust pricing-expression library.
- Language
- Go
- Database
- PostgreSQL
- Hosting
- Docker
- License
- AGPL-3.0
- Running for
- 3 years 2 months
- Team
- 6-20 people
Why this architecture
Usage-event ingestion and pricing-expression evaluation are pulled out of the Rails request/response cycle into a dedicated Go service and a shared Rust library, so billing-critical event throughput scales independently of the API that serves the dashboard and public API.
Tech stack by layer
8 technologies · audited Sep 17, 2026- ReactBilling dashboard UI, versioned as the "front" git submodule and built with pnpm in the all-in-one Docker image.(inferred)
- Ruby on RailsMain billing API (subscriptions, invoices, customers), versioned as the "api" git submodule; Sidekiq runs its background jobs.
- Go events-processorStandalone service that consumes usage events from Kafka and writes aggregated results to Postgres via GORM/pgx.
- Redpanda ConnectRuns the connectors service that ingests events from HTTP, AWS Kinesis, and AWS SQS into Kafka via YAML pipeline configs.
- lago-expression (Rust)Shared pricing/expression-evaluation library, compiled to a native .so and linked into both the Go events-processor and the Ruby API image.
- PostgreSQLPrimary datastore for billing data, using a partitioning-enabled image/package for time-series usage tables.
- RedisBacks Rails caching, Sidekiq job queues, and ActionCable, per the REDIS_URL/LAGO_REDIS_CACHE_URL/LAGO_SIDEKIQ_WEB environment variables.
- KafkaStreaming backbone between event ingestion (connectors) and processing (events-processor), with Debezium and topic-creation scripts alongside it.
- Docker ComposeRuns the getlago/api and getlago/front images with Postgres and Redis for local/self-hosted deployment.
- S3 / GCSOptional object storage backends for generated files, toggled by LAGO_USE_AWS_S3 / LAGO_USE_GCS environment variables.
Lago architecture diagram
Open SVGDiagram as text
- Dashboard (React (front submodule)) → Billing API (Rails (api submodule)): REST
- Your backend (usage events · API) → Billing API (Rails (api submodule)): REST
- Your backend (usage events · API) → Connectors (Redpanda Connect): Kinesis · SQS
- Connectors (Redpanda Connect) → Kafka (event stream): produce
- Kafka (event stream) → events-processor (Go · lago-expression): consume
- events-processor (Go · lago-expression) → PostgreSQL (billing · partitioned usage): aggregates
- Billing API (Rails (api submodule)) → PostgreSQL (billing · partitioned usage): ActiveRecord
- Billing API (Rails (api submodule)) → Redis (cache · queues): enqueue
- Redis (cache · queues) → Sidekiq (background jobs): jobs
- Sidekiq (background jobs) → Object storage (S3 / GCS (optional)): invoices · exports
Key architectural decisions
7 decisions- 01
High-throughput usage-event ingestion runs in a separate Go service
events-processor/go.mod defines a standalone Go module (github.com/getlago/lago/events-processor) using franz-go for Kafka, gorm/pgx for Postgres, go-redis for Redis, and badger for local caching, keeping event processing off the Ruby API process entirely.
- 02
Events reach Kafka through configurable Redpanda Connect pipelines, not custom code
connectors/Dockerfile builds FROM docker.io/redpandadata/connect and copies in connectors/http.yml, connectors/kinesis.yml, and connectors/sqs.yml, so adding an ingestion source is a YAML pipeline change rather than a new service.
- 03
Pricing-expression evaluation is a shared Rust library, not duplicated per language
events-processor/Dockerfile has a rust-build stage that clones getlago/lago-expression and compiles libexpression_go.so, linked into the Go binary; docker/Dockerfile installs a Rust toolchain (curl https://sh.rustup.rs) into the Ruby API build image for the same library, so both the API and the event processor evaluate pricing formulas with one implementation.
- 04
PostgreSQL ships with native time-based partitioning built in
docker-compose.yml uses image: getlago/postgres-partman:15.0-alpine as the database image, and docker/Dockerfile installs the postgresql-17-partman package in the all-in-one image, so high-volume usage-event tables can be partitioned by time without operators installing the extension themselves.
- 05
Rails background jobs and caching share one Redis instance by default
docker-compose.yml sets REDIS_URL, LAGO_REDIS_CACHE_URL, and LAGO_SIDEKIQ_WEB on the backend service against the same redis container, so Sidekiq queues, general caching, and (per LAGO_REDIS_CABLE_URL) ActionCable all default to one Redis deployment unless separately configured.
- 06
PDF generation and analytics are separate services reached over HTTP, not in-process
docker-compose.yml configures LAGO_PDF_URL (default http://pdf:3000) and LAGO_DATA_API_URL (default http://data-api) as environment variables the Rails API calls out to, rather than generating PDFs or serving revenue-analytics data from within the main API process.
- 07
The Rails API and React frontend are git submodules, not native to this repository
.gitmodules and docker/Dockerfile (COPY ./front/ for the Node build stage, COPY ./api/Gemfile for the Ruby build stage) show that this repository assembles and deploys the api and front codebases as submodules rather than containing their source directly.
How Lago is built
How Lago is structured
This repository is the assembly and deployment layer for Lago rather than a single application tree: .gitmodules pulls in api/ (the Ruby on Rails billing API) and front/ (the React dashboard) as separate git submodules, so their source is not part of this repository's own commit history or language statistics — the language breakdown here (Go, Shell, Dockerfile, Procfile) reflects only the code that does live in this repo. That code covers three areas: events-processor/ (a standalone Go service for usage-event processing), connectors/ (Redpanda Connect pipeline configs for event ingestion), and the deployment tooling itself — docker-compose.yml, docker-compose.dev.yml, deploy/ (production and "light" compose variants plus a deploy.sh script), docker/ (the all-in-one image build), and extra/ (Kafka Connect and Debezium configuration, TLS setup scripts).
Frontend
The dashboard lives in the front/ submodule and is not directly inspectable from this repository, but docker/Dockerfile's front_build stage shows it is built with Node 24 and pnpm (corepack prepare pnpm@latest --activate && pnpm install && pnpm build) into static assets served by the all-in-one image's nginx. The GitHub repository topics list react, consistent with the Dockerfile's Node-based build step.
Backend & APIs
The core API lives in the api/ submodule and is built in docker/Dockerfile's api_build stage from ruby:4.0.6-slim, installing api/Gemfile/api/Gemfile.lock with Bundler and compiling a Rust toolchain into the image (curl https://sh.rustup.rs | bash) for the shared lago-expression pricing library. docker-compose.yml configures the API container with RAILS_ENV: production, a SECRET_KEY_BASE, and LAGO_SIDEKIQ_WEB, indicating Sidekiq handles background jobs. Two more services extend the API rather than living inside it: events-processor/go.mod is a Go module using twmb/franz-go (Kafka client), gorm.io/gorm/jackc/pgx (Postgres), redis/go-redis (Redis), and dgraph-io/badger (embedded key-value cache) to consume and aggregate usage events; and connectors/ runs Redpanda Connect (docker.io/redpandadata/connect base image in connectors/Dockerfile) configured by connectors/http.yml, connectors/kinesis.yml, and connectors/sqs.yml to pull events from HTTP webhooks, AWS Kinesis, and AWS SQS into Kafka.
Data & persistence
PostgreSQL is the system of record; docker-compose.yml pins it to getlago/postgres-partman:15.0-alpine, a Postgres image bundled with the pg_partman extension, and docker/Dockerfile's all-in-one build installs postgresql-17-partman directly — both point at time-based table partitioning for high-volume usage-event data. Redis is configured for multiple roles from environment variables in docker-compose.yml: REDIS_URL for general use, LAGO_REDIS_CACHE_URL for caching, and (optionally) LAGO_REDIS_CABLE_URL for ActionCable, alongside backing Sidekiq queues. Kafka is the streaming layer between connectors and events-processor; extra/debezium_config.json and extra/kafka-connect suggest change-data-capture from Postgres into Kafka is also part of the pipeline, and scripts/create-topics.sh provisions the required topics.
Build, test & deploy
.github/workflows drives CI for the pieces that live in this repository (the facts gather resolved workflows including events-processor-tests.yml, build-connectors-image.yml, build-processors-image.yml, docker-ci.yml, and several release workflows for Docker images). events-processor/Dockerfile is a three-stage build: a Rust stage compiling lago-expression to libexpression_go.so, a Go stage building the event_processors binary against that shared library, and a slim debian:13-slim runtime stage. connectors/Dockerfile is a single-stage build on the Redpanda Connect base image with the YAML pipeline configs copied in. The all-in-one docker/Dockerfile builds the Ruby API and React front separately, then assembles a runtime image that additionally installs nginx, postgresql-17-partman, redis-server, and even Docker itself (docker-ce), reflecting a self-contained "everything in one container" deployment target distinct from the multi-container docker-compose.yml path.
Self-hosting notes
docker-compose.yml is the standard self-hosting entry point, running the published getlago/api and getlago/front images against Postgres and Redis containers, with environment variables covering S3/GCS object storage (LAGO_USE_AWS_S3, LAGO_USE_GCS), SMTP (LAGO_SMTP_*), and references to two services this repository does not build itself: a PDF generator (LAGO_PDF_URL, default http://pdf:3000) and a data/analytics API (LAGO_DATA_API_URL, default http://data-api). deploy/docker-compose.production.yml and deploy/docker-compose.light.yml provide alternative topologies, with deploy/deploy.sh scripting the rollout. extra/init-letsencrypt.sh and extra/init-selfsigned.sh, plus traefik/traefik.yml and traefik/dynamic.yml, show TLS termination is expected to run through Traefik in front of the stack.
What to copy (and what not to)
Pulling usage-event ingestion out of the primary Rails API into a dedicated Go service — fed by a swappable set of Redpanda Connect pipelines rather than bespoke ingestion code per source — is a strong pattern for any billing or metering product that expects event volume to outpace normal API traffic; a lower-volume CRUD app would not need a separate streaming pipeline at all. Sharing one Rust pricing-expression library between the Ruby API and the Go event processor, rather than reimplementing pricing logic twice, is worth copying whenever the same business logic must run correctly in more than one language runtime. Versioning the API and frontend as git submodules keeps this deployment repository lean, but it also means anyone auditing "the whole stack" from this repository alone only sees the assembly layer — a real review of the Rails API or React app requires cloning those submodules directly rather than trusting what is visible here.
Sources & repo audit
- docker-compose.yml (getlago/api, getlago/front images, Postgres partman, Redis, S3/GCS env)
- docker/Dockerfile (all-in-one build: Rails API, React front, Postgres, Redis, Rust)
- events-processor/go.mod (franz-go/Kafka, gorm/pgx, go-redis, badger, OpenTelemetry)
- events-processor/Dockerfile (Rust lago-expression build stage)
- connectors/Dockerfile (Redpanda Connect base image)
Independent analysis of repository at github.com/getlago/lago. Spotted an inaccuracy? Use the claim form to request a correction.
Show this stack on your README or website
[](https://stackitfast.com/project/lago)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 Lago'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 "Lago" 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 **Lago**.
---
## 1. PROJECT SPECIFICATIONS & BENCHMARK
- **Reference Architecture**: Lago
- **What It Does**: Lago is an open-source metering and usage-based billing platform: a Ruby on Rails API and React dashboard (versioned as git submodules) handle subscriptions and invoicing, while a separate Go service and Kafka-based connectors ingest and process high-volume usage events, sharing a Rust pricing-expression library.
- **Domain & Category**: Billing & Metering Engine
- **Production Scale**: 6-20 people
- **Development Mode**: CLASSIC
- **Architectural Rationale**: Usage-event ingestion and pricing-expression evaluation are pulled out of the Rails request/response cycle into a dedicated Go service and a shared Rust library, so billing-critical event throughput scales independently of the API that serves the dashboard and public API.
- **Live Website Reference**: https://getlago.com
- **Source Repository**: https://github.com/getlago/lago
---
## 2. PRODUCTION TECH STACK
- **Full Stack Array**: Rails, React, Go, PostgreSQL, Redis, Kafka, Rust, Docker
- **Primary Language(s)**: Go, Shell, Dockerfile
- **License of the reference repo**: AGPL-3.0
- **Frontend**: React — Billing dashboard UI, versioned as the "front" git submodule and built with pnpm in the all-in-one Docker image.
- **Backend & APIs**: Ruby on Rails — Main billing API (subscriptions, invoices, customers), versioned as the "api" git submodule; Sidekiq runs its background jobs.; Go events-processor — Standalone service that consumes usage events from Kafka and writes aggregated results to Postgres via GORM/pgx.; Redpanda Connect — Runs the connectors service that ingests events from HTTP, AWS Kinesis, and AWS SQS into Kafka via YAML pipeline configs.; lago-expression (Rust) — Shared pricing/expression-evaluation library, compiled to a native .so and linked into both the Go events-processor and the Ruby API image.
- **Data & persistence**: PostgreSQL — Primary datastore for billing data, using a partitioning-enabled image/package for time-series usage tables.; Redis — Backs Rails caching, Sidekiq job queues, and ActionCable, per the REDIS_URL/LAGO_REDIS_CACHE_URL/LAGO_SIDEKIQ_WEB environment variables.; Kafka — Streaming backbone between event ingestion (connectors) and processing (events-processor), with Debezium and topic-creation scripts alongside it.
- **Infrastructure & deploy**: Docker Compose — Runs the getlago/api and getlago/front images with Postgres and Redis for local/self-hosted deployment.; S3 / GCS — Optional object storage backends for generated files, toggled by LAGO_USE_AWS_S3 / LAGO_USE_GCS environment variables.
---
## 3. KEY ARCHITECTURAL DECISIONS (audited from https://github.com/getlago/lago @ eb58675)
1. **High-throughput usage-event ingestion runs in a separate Go service**: events-processor/go.mod defines a standalone Go module (github.com/getlago/lago/events-processor) using franz-go for Kafka, gorm/pgx for Postgres, go-redis for Redis, and badger for local caching, keeping event processing off the Ruby API process entirely.
2. **Events reach Kafka through configurable Redpanda Connect pipelines, not custom code**: connectors/Dockerfile builds FROM docker.io/redpandadata/connect and copies in connectors/http.yml, connectors/kinesis.yml, and connectors/sqs.yml, so adding an ingestion source is a YAML pipeline change rather than a new service.
3. **Pricing-expression evaluation is a shared Rust library, not duplicated per language**: events-processor/Dockerfile has a rust-build stage that clones getlago/lago-expression and compiles libexpression_go.so, linked into the Go binary; docker/Dockerfile installs a Rust toolchain (curl https://sh.rustup.rs) into the Ruby API build image for the same library, so both the API and the event processor evaluate pricing formulas with one implementation.
4. **PostgreSQL ships with native time-based partitioning built in**: docker-compose.yml uses image: getlago/postgres-partman:15.0-alpine as the database image, and docker/Dockerfile installs the postgresql-17-partman package in the all-in-one image, so high-volume usage-event tables can be partitioned by time without operators installing the extension themselves.
5. **Rails background jobs and caching share one Redis instance by default**: docker-compose.yml sets REDIS_URL, LAGO_REDIS_CACHE_URL, and LAGO_SIDEKIQ_WEB on the backend service against the same redis container, so Sidekiq queues, general caching, and (per LAGO_REDIS_CABLE_URL) ActionCable all default to one Redis deployment unless separately configured.
6. **PDF generation and analytics are separate services reached over HTTP, not in-process**: docker-compose.yml configures LAGO_PDF_URL (default http://pdf:3000) and LAGO_DATA_API_URL (default http://data-api) as environment variables the Rails API calls out to, rather than generating PDFs or serving revenue-analytics data from within the main API process.
7. **The Rails API and React frontend are git submodules, not native to this repository**: .gitmodules and docker/Dockerfile (COPY ./front/ for the Node build stage, COPY ./api/Gemfile for the Ruby build stage) show that this repository assembles and deploys the api and front codebases as submodules rather than containing their source directly.
---
## 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, Redis, Kafka): 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 Lago
What is Lago built with?
The billing API is Ruby on Rails and the dashboard is React, both pulled in as git submodules; a separate Go service (events-processor) ingests usage events from Kafka, and a shared Rust library (lago-expression) evaluates pricing formulas for both the API and the event processor.
Does Lago need a backend?
Yes. The Rails API (versioned as the "api" submodule, built in docker/Dockerfile) is the core billing backend, storing data in PostgreSQL and using Redis for Sidekiq jobs and caching; the React frontend is a client of that API.
Can I self-host Lago?
Yes. docker-compose.yml runs the published getlago/api and getlago/front images against a Postgres (getlago/postgres-partman) and Redis container, and deploy/docker-compose.production.yml and deploy/docker-compose.light.yml offer alternative deployment topologies.
How does Lago ingest usage events at scale?
Events land in Kafka through Redpanda Connect pipelines defined in connectors/http.yml, connectors/kinesis.yml, and connectors/sqs.yml, and a dedicated Go service (events-processor/go.mod, using franz-go) consumes and aggregates them into PostgreSQL rather than the Rails API handling ingestion directly.
Does Lago use Kafka?
Yes. The events-processor Go service depends on twmb/franz-go, a Kafka client, and the connectors service publishes into Kafka via Redpanda Connect pipelines; the repository also ships extra/kafka-connect and a Debezium config for change-data-capture from Postgres.
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
- InfisicalHybridSecret Management · 6-20 peopleShares React · Go · PostgreSQL
- PostHogHybridProduct Analytics Platform · 20+ peopleShares React · PostgreSQL · Kafka
- HyperswitchClassicOpen-Source Payments Orchestration · 20+ peopleShares Rust · PostgreSQL · Redis
- MedusaClassicE-commerce API · 20+ peopleShares React · PostgreSQL · Redis
- PenpotClassicDesign & Prototyping Platform · 20+ peopleShares Rust · PostgreSQL · Redis
- HoppscotchClassicAPI Testing Client · 6-20 peopleShares PostgreSQL · Redis · Rust