# Lago

Metering and usage-based billing API with subscription management.

- Replaces: [Chargebee](https://openlogs.fyi/alternatives/chargebee), [Stripe Billing](https://openlogs.fyi/alternatives/stripe-billing)
- Summary: Paid usage-based billing services, such as Stripe Billing and Chargebee.
- Stack: Go (language), Redis (database)
- Deployment: docker_compose
- License: AGPL-3.0 (osi) [source](https://github.com/getlago/lago/blob/117b8219da7ad92631d47018ace3896f26201161/LICENSE)
- Business model: unknown. The README at the pinned commit does not state a business model, so it stays unknown. ([source](https://github.com/getlago/lago/blob/117b8219da7ad92631d47018ace3896f26201161/README.md), read 2026-10-22)
- Difficulty: hard. 8 services (1 database or broker: redis), 3 volume mounts, 5 env values to set, no elevated host access. Heuristic score 7 of 11: hard.
- Requirements: not stated
- Repository: [getlago/lago](https://github.com/getlago/lago)

## Install

1. Docker with the Compose plugin ([source](https://github.com/getlago/lago/blob/117b8219da7ad92631d47018ace3896f26201161/docker-compose.yml))

   ```text
   A host with Docker and the Docker Compose plugin. This entry runs from a compose file.
   ```

2. Save the compose file as docker-compose.yml ([source](https://github.com/getlago/lago/blob/117b8219da7ad92631d47018ace3896f26201161/docker-compose.yml))

   ```yaml
   volumes:
     lago_postgres_data:
     lago_redis_data:
     lago_storage_data:
   
   x-postgres-image: &postgres-image
     image: getlago/postgres-partman:15.0-alpine
   x-redis-image: &redis-image
     image: redis:7-alpine
   x-backend-image: &backend-image
     image: getlago/api:v1.55.0
   x-frontend-image: &frontend-image
     image: getlago/front:v1.55.0
   
   x-lago-api-url: &lago-api-url
     "LAGO_API_URL": ${LAGO_API_URL:-http://localhost:3000}
   x-lago-front-url: &lago-front-url
     "LAGO_FRONT_URL": ${LAGO_FRONT_URL:-http://localhost}
   
   x-backend-environment: &backend-env
     "DATABASE_URL": postgresql://${POSTGRES_USER:-lago}:${POSTGRES_PASSWORD:-changeme}@${POSTGRES_HOST:-db}:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-lago}?search_path=${POSTGRES_SCHEMA:-public}
     "REDIS_URL": redis://${REDIS_HOST:-redis}:${REDIS_PORT:-6379}
     "REDIS_PASSWORD": ${REDIS_PASSWORD:-}
     "LAGO_REDIS_SIDEKIQ_SENTINELS": ${LAGO_REDIS_SIDEKIQ_SENTINELS:-}
     "LAGO_REDIS_SIDEKIQ_MASTER_NAME": ${LAGO_REDIS_SIDEKIQ_MASTER_NAME:-master}
     "LAGO_REDIS_SIDEKIQ_SENTINEL_USERNAME": ${LAGO_REDIS_SIDEKIQ_SENTINEL_USERNAME:-}
     "LAGO_REDIS_SIDEKIQ_SENTINEL_PASSWORD": ${LAGO_REDIS_SIDEKIQ_SENTINEL_PASSWORD:-}
     "SECRET_KEY_BASE": ${SECRET_KEY_BASE:-your-secret-key-base-hex-64}
     "RAILS_ENV": production
     "RAILS_LOG_TO_STDOUT": ${LAGO_RAILS_STDOUT:-true}
     "LAGO_RSA_PRIVATE_KEY": ${LAGO_RSA_PRIVATE_KEY}
     "LAGO_SIDEKIQ_WEB": ${LAGO_SIDEKIQ_WEB:-true}
     "LAGO_ENCRYPTION_PRIMARY_KEY": ${LAGO_ENCRYPTION_PRIMARY_KEY:-your-encryption-primary-key}
     "LAGO_ENCRYPTION_DETERMINISTIC_KEY": ${LAGO_ENCRYPTION_DETERMINISTIC_KEY:-your-encryption-deterministic-key}
     "LAGO_ENCRYPTION_KEY_DERIVATION_SALT": ${LAGO_ENCRYPTION_KEY_DERIVATION_SALT:-your-encryption-derivation-salt}
     "LAGO_USE_AWS_S3": ${LAGO_USE_AWS_S3:-false}
     "LAGO_AWS_S3_ACCESS_KEY_ID": ${LAGO_AWS_S3_ACCESS_KEY_ID:-azerty123456}
     "LAGO_AWS_S3_SECRET_ACCESS_KEY": ${LAGO_AWS_S3_SECRET_ACCESS_KEY:-azerty123456}
     "LAGO_AWS_S3_REGION": ${LAGO_AWS_S3_REGION:-us-east-1}
     "LAGO_AWS_S3_BUCKET": ${LAGO_AWS_S3_BUCKET:-bucket}
     "LAGO_AWS_S3_ENDPOINT": ${LAGO_AWS_S3_ENDPOINT}
     "LAGO_USE_GCS": ${LAGO_USE_GCS:-false}
     "LAGO_GCS_PROJECT": ${LAGO_GCS_PROJECT:-}
     "LAGO_GCS_BUCKET": ${LAGO_GCS_BUCKET:-}
     "LAGO_FROM_EMAIL": ${LAGO_FROM_EMAIL:-}
     "LAGO_SMTP_ADDRESS": ${LAGO_SMTP_ADDRESS:-}
     "LAGO_SMTP_PORT": ${LAGO_SMTP_PORT:-587}
     "LAGO_SMTP_USERNAME": ${LAGO_SMTP_USERNAME:-}
     "LAGO_SMTP_PASSWORD": ${LAGO_SMTP_PASSWORD:-}
     "LAGO_PDF_URL": ${LAGO_PDF_URL:-http://pdf:3000}
     "LAGO_DATA_API_URL": ${LAGO_DATA_API_URL:-http://data-api}
     "LAGO_DATA_API_BEARER_TOKEN": ${LAGO_DATA_API_BEARER_TOKEN:-}
     "LAGO_REDIS_CACHE_URL": redis://${LAGO_REDIS_CACHE_HOST:-redis}:${LAGO_REDIS_CACHE_PORT:-6379}
     "LAGO_REDIS_CACHE_PASSWORD": ${LAGO_REDIS_CACHE_PASSWORD}
     "LAGO_REDIS_CACHE_SENTINELS": ${LAGO_REDIS_CACHE_SENTINELS:-}
     "LAGO_REDIS_CACHE_MASTER_NAME": ${LAGO_REDIS_CACHE_MASTER_NAME:-master}
     "LAGO_REDIS_CACHE_SENTINEL_USERNAME": ${LAGO_REDIS_CACHE_SENTINEL_USERNAME:-}
     "LAGO_REDIS_CACHE_SENTINEL_PASSWORD": ${LAGO_REDIS_CACHE_SENTINEL_PASSWORD:-}
     "LAGO_REDIS_CABLE_URL": ${LAGO_REDIS_CABLE_URL:-}
     "LAGO_DISABLE_SEGMENT": ${LAGO_DISABLE_SEGMENT}
     "LAGO_DISABLE_WALLET_REFRESH": ${LAGO_DISABLE_WALLET_REFRESH}
     "LAGO_DISABLE_SIGNUP": ${LAGO_DISABLE_SIGNUP:-false}
     "LAGO_WEBHOOK_ALLOW_PRIVATE_URLS": ${LAGO_WEBHOOK_ALLOW_PRIVATE_URLS:-false}
     "LAGO_DISABLE_PDF_GENERATION": ${LAGO_DISABLE_PDF_GENERATION:-false}
     "LAGO_OAUTH_PROXY_URL": https://proxy.getlago.com
     "LAGO_LICENSE": ${LAGO_LICENSE:-}
     "LAGO_CREATE_ORG": ${LAGO_CREATE_ORG:-false}
     "LAGO_ORG_USER_PASSWORD": ${LAGO_ORG_USER_PASSWORD:-}
     "LAGO_ORG_USER_EMAIL": ${LAGO_ORG_USER_EMAIL:-}
     "LAGO_ORG_NAME": ${LAGO_ORG_NAME:-}
     "LAGO_ORG_API_KEY": ${LAGO_ORG_API_KEY:-}
     "GOOGLE_AUTH_CLIENT_ID": ${GOOGLE_AUTH_CLIENT_ID:-}
     "GOOGLE_AUTH_CLIENT_SECRET": ${GOOGLE_AUTH_CLIENT_SECRET:-}
     "MISTRAL_API_KEY": ${MISTRAL_API_KEY:-}
     "MISTRAL_AGENT_ID": ${MISTRAL_AGENT_ID:-}
     # - SIDEKIQ_EVENTS=true
     # - SIDEKIQ_PDFS=true
     # - SIDEKIQ_BILLING=true
     # - SIDEKIQ_CLOCK=true
     # - SIDEKIQ_WEBHOOK=true
     # - SIDEKIQ_AI_AGENT=true
     # - SIDEKIQ_ALERTS=true
   x-frontend-environment: &frontend-env
     "API_URL": ${LAGO_API_URL:-http://localhost:3000}
     "APP_ENV": production
     "LAGO_OAUTH_PROXY_URL": https://proxy.getlago.com
     "LAGO_DISABLE_PDF_GENERATION": ${LAGO_DISABLE_PDF_GENERATION:-false}
   
   services:
     db:
       <<: *postgres-image
       container_name: lago-db
       restart: unless-stopped
       healthcheck:
         test:
           [
             "CMD-SHELL",
             "pg_isready -U ${POSTGRES_USER:-lago} -d ${POSTGRES_DB:-lago} -h localhost -p ${POSTGRES_PORT:-5432}",
           ]
         interval: 10s
         timeout: 5s
         retries: 5
       environment:
         POSTGRES_DB: ${POSTGRES_DB:-lago}
         POSTGRES_USER: ${POSTGRES_USER:-lago}
         POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-changeme}
         PGDATA: /data/postgres
         PGPORT: ${POSTGRES_PORT:-5432}
         POSTGRES_SCHEMA: public
       volumes:
         - lago_postgres_data:/data/postgres
       ports:
         - ${POSTGRES_PORT:-5432}:${POSTGRES_PORT:-5432}
   
     redis:
       <<: *redis-image
       container_name: lago-redis
       restart: unless-stopped
       healthcheck:
         test: ["CMD", "redis-cli", "-p", "${REDIS_PORT:-6379}", "ping"]
         interval: 10s
         timeout: 5s
         retries: 5
       command: --port ${REDIS_PORT:-6379}
       volumes:
         - lago_redis_data:/data
       ports:
         - ${REDIS_PORT:-6379}:${REDIS_PORT:-6379}
   
     migrate:
       <<: *backend-image
       container_name: lago-migrate
       depends_on:
         db:
           condition: service_healthy
           restart: true
       command: ["./scripts/migrate.sh"]
       environment:
         <<: *backend-env
   
     api:
       <<: *backend-image
       container_name: lago-api
       restart: unless-stopped
       depends_on:
         migrate:
           condition: service_completed_successfully
         db:
           condition: service_healthy
           restart: true
         redis:
           condition: service_healthy
           restart: true
       command: ["./scripts/start.api.sh"]
       healthcheck:
         test: curl -f http://localhost:3000/health || exit 1
         interval: 10s
         start_period: 30s
         timeout: 60s
         start_interval: 2s
       environment:
         <<: [*backend-env, *lago-api-url, *lago-front-url]
       volumes:
         - lago_storage_data:/app/storage
       ports:
         - ${API_PORT:-3000}:3000
   
     front:
       <<: *frontend-image
       container_name: lago-front
       restart: unless-stopped
       # Use this command if you want to use SSL with Let's Encrypt
       # command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
       depends_on:
         api:
           condition: service_healthy
           restart: true
       environment:
         <<: [*frontend-env, *lago-api-url, *lago-front-url]
       ports:
         - ${FRONT_PORT:-80}:80
       #  - 443:443
       # Using SSL with Let's Encrypt
       # volumes:
       #   - ./extra/nginx-letsencrypt.conf:/etc/nginx/conf.d/default.conf
       #   - ./extra/certbot/conf:/etc/letsencrypt
       #   - ./extra/certbot/www:/var/www/certbot
       # Using SSL with self signed certificates
       # volumes:
       #   - ./extra/nginx-selfsigned.conf:/etc/nginx/conf.d/default.conf
       #   - ./extra/ssl/nginx-selfsigned.crt:/etc/ssl/certs/nginx-selfsigned.crt
       #   - ./extra/ssl/nginx-selfsigned.key:/etc/ssl/private/nginx-selfsigned.key
       #   - ./extra/ssl/dhparam.pem:/etc/ssl/certs/dhparam.pem
   
     # Only used for SSL support with Let's Encrypt
     # certbot:
     #   image: certbot/certbot
     #   entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
     #   volumes:
     #     - ./extra/certbot/conf:/etc/letsencrypt
     #     - ./extra/certbot/www:/var/www/certbot
   
     api-worker:
       <<: *backend-image
       container_name: lago-worker
       restart: unless-stopped
       depends_on:
         migrate:
           condition: service_completed_successfully
         db:
           condition: service_healthy
           restart: true
         redis:
           condition: service_healthy
           restart: true
       command: ["./scripts/start.worker.sh"]
       healthcheck:
         test: curl -f http://localhost:8080 || exit 1
         interval: 10s
         start_period: 30s
         timeout: 60s
         start_interval: 2s
       environment:
         <<: [*backend-env, *lago-api-url]
       volumes:
         - lago_storage_data:/app/storage
   
     # You can uncomment this if you want to use a dedicated Sidekiq worker for the event ingestion.
     # It is recommendend if you have a high usage of events to not impact the other Sidekiq Jobs.
     #api-events-worker:
     #  <<: *backend-image
     #  container_name: lago-events-worker
     #  restart: unless-stopped
     #  depends_on:
     #    api:
     #      condition: service_healthy
     #  command: ["./scripts/start.events.worker.sh"]
     #  environment:
     #    <<: [*backend-env, *lago-api-url]
   
     # You can uncomment this if you want to use a dedicated Sidekiq worker for the usage monitoring & alerting jobs.
     # It is recommended if you have a high usage of alerts to not impact the other Sidekiq Jobs.
     # Set SIDEKIQ_ALERTS=true on the other workers so alerting jobs are routed to this worker.
     #api-alerts-worker:
     #  <<: *backend-image
     #  container_name: lago-alerts-worker
     #  restart: unless-stopped
     #  depends_on:
     #    api:
     #      condition: service_healthy
     #  command: ["./scripts/start.alerts.worker.sh"]
     #  environment:
     #    <<: [*backend-env, *lago-api-url]
   
     # You can uncomment this if you want to use a dedicated Sidekiq worker for the invoices pdf creation.
     # It is recommended if you have a high usage of invoices being created to not impact the other Sidekiq Jobs.
     #api-pdfs-worker:
     #  <<: *backend-image
     #  container_name: lago-pdfs-worker
     #  restart: unless-stopped
     #  depends_on:
     #    api:
     #      condition: service_healthy
     #  command: ["./scripts/start.pdfs.worker.sh"]
     #  environment:
     #    <<: [*backend-env, *lago-api-url]
   
     # You can uncomment this if you want to use a dedicated Sidekiq worker for the invoices creation.
     # It is recommended if you have a high usage of invoices being created to not impact the other Sidekiq Jobs.
     #api-billing-worker:
     #  <<: *backend-image
     #  container_name: lago-billing-worker
     #  restart: unless-stopped
     #  depends_on:
     #    api:
     #      condition: service_healthy
     #  command: ["./scripts/start.billing.worker.sh"]
     #  environment:
     #    <<: [*backend-env, *lago-api-url]
   
     # You can uncomment this if you want to use a dedicated Sidekiq worker for the clock jobs.
     #api-clock-worker:
     #  <<: *backend-image
     #  container_name: lago-clock-worker
     #  restart: unless-stopped
     #  depends_on:
     #    api:
     #      condition: service_healthy
     #  command: ["./scripts/start.clock.worker.sh"]
     #  environment:
     #    <<: [*backend-env, *lago-api-url]
   
     # You can uncomment this if you want to use a dedicated Sidekiq worker for the webhook jobs.
     #api-webhook-worker:
     #  <<: *backend-image
     #  container_name: lago-webhook-worker
     #  restart: unless-stopped
     #  depends_on:
     #    api:
     #      condition: service_healthy
     #  command: ["./scripts/start.webhook.worker.sh"]
     #  environment:
     #    <<: [*backend-env, *lago-api-url]
   
     # You can uncomment this if you want to use a dedicated Sidekiq worker for the analytics-related jobs.
     #api-analytics-worker:
     #  <<: *backend-image
     #  container_name: api-analytics-worker
     #  restart: unless-stopped
     #  depends_on:
     #    api:
     #      condition: service_healthy
     #  command: ["./scripts/start.analytics.worker.sh"]
     #  environment:
     #    <<: [*backend-env, *lago-api-url]
   
     # You can uncomment this if you want to use a dedicated Sidekiq worker for the AI Agent-related jobs.
     #api-ai-agent-worker:
     #  <<: *backend-image
     #  container_name: api-ai-agent-worker
     #  restart: unless-stopped
     #  depends_on:
     #    api:
     #      condition: service_healthy
     #  command: ["./scripts/start.ai_agent.worker.sh"]
     #  environment:
     #    <<: [*backend-env, *lago-api-url]
   
     api-clock:
       <<: *backend-image
       container_name: lago-clock
       restart: unless-stopped
       depends_on:
         migrate:
           condition: service_completed_successfully
         db:
           condition: service_healthy
           restart: true
         redis:
           condition: service_healthy
           restart: true
       command: ["./scripts/start.clock.sh"]
       environment:
         <<: [*backend-env, *lago-api-url]
   
     pdf:
       image: getlago/lago-gotenberg:7.8.2
   ```

3. Set the values the compose file needs ([source](https://github.com/getlago/lago/blob/117b8219da7ad92631d47018ace3896f26201161/docker-compose.yml))

   ```text
   These variables have no default in the compose file: LAGO_AWS_S3_ENDPOINT, LAGO_DISABLE_SEGMENT, LAGO_DISABLE_WALLET_REFRESH, LAGO_REDIS_CACHE_PASSWORD, LAGO_RSA_PRIVATE_KEY. Compose reads them from the shell or from a .env file next to the compose file. Replace the placeholder values listed under Gotchas.
   ```

4. Start it ([source](https://github.com/getlago/lago/blob/117b8219da7ad92631d47018ace3896f26201161/docker-compose.yml))

   ```bash
   docker compose up -d
   ```

5. Open the app ([source](https://github.com/getlago/lago/blob/117b8219da7ad92631d47018ace3896f26201161/docker-compose.yml))

   ```text
   Open http://localhost:80. The compose file publishes host port 80 for the front service.
   ```

Compose file check: config_ok on 2026-10-22.

## Gotchas and maintenance

### Gotchas

- The compose file has placeholder values: Placeholder values: POSTGRES_PASSWORD is "changeme" (db), DATABASE_URL is "changeme" (migrate), DATABASE_URL is "changeme" (api), DATABASE_URL is "changeme" (api-worker), DATABASE_URL is "changeme" (api-clock). ([source](https://github.com/getlago/lago/blob/117b8219da7ad92631d47018ace3896f26201161/docker-compose.yml), checked 2026-10-22)
- The compose file publishes host ports: Host ports: 5432 (db), 6379 (redis), 3000 (api), 80 (front). A port without a host address listens on all host network interfaces. ([source](https://github.com/getlago/lago/blob/117b8219da7ad92631d47018ace3896f26201161/docker-compose.yml), checked 2026-10-22)

### Signals

Computed from a GitHub snapshot taken 2026-10-22.

- Open issues and pull requests: 33
- Stars: 10705, forks: 773
- Last push: 2026-10-21 (0 days ago)
- Latest release: v1.55.0 on 2026-10-07 (14 days ago)

## Sources

- [Repository: https://github.com/getlago/lago](https://github.com/getlago/lago)
- [License: https://github.com/getlago/lago/blob/117b8219da7ad92631d47018ace3896f26201161/LICENSE](https://github.com/getlago/lago/blob/117b8219da7ad92631d47018ace3896f26201161/LICENSE)
- [Business model: https://github.com/getlago/lago/blob/117b8219da7ad92631d47018ace3896f26201161/README.md](https://github.com/getlago/lago/blob/117b8219da7ad92631d47018ace3896f26201161/README.md)
- [Compose file: https://github.com/getlago/lago/blob/117b8219da7ad92631d47018ace3896f26201161/docker-compose.yml](https://github.com/getlago/lago/blob/117b8219da7ad92631d47018ace3896f26201161/docker-compose.yml)

---
Canonical: https://openlogs.fyi/tools/lago  
Last updated: 2026-10-22
