Lago
Metering and usage-based billing API with subscription management.
- Replaces
-
Paid usage-based billing services, such as Stripe Billing and Chargebee.
All open-source alternatives to Chargebee, Stripe Billing
- Difficulty
-
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.
- Stack
Go (language), Redis (database)
- Deployment
docker_compose
- Requirements
Not stated.
- License
-
AGPL-3.0(OSI-approved open source) - Business model
-
Unknown
The README at the pinned commit does not state a business model, so it stays unknown.
Source: github.com/getlago/lago/blob/117b821/README.md, read on .
- Repository
GitHub stats
- Stars
- 10,705
- Forks
- 773
- Open issues and pull requests
- 33
- Last push
- 2026-10-21
- Latest release
- v1.55.0
- Released
- 2026-10-07
From the GitHub API on . These numbers are a snapshot, not live. GitHub detected the license as AGPL-3.0.
Compose file
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
From the app's own repository: github.com/getlago/lago/blob/117b821/docker-compose.yml.
Compose file from the app repository.
Install
- Docker with the Compose plugin (github.com/getlago/lago/blob/117b821/docker-compose.yml)
A host with Docker and the Docker Compose plugin. This entry runs from a compose file.
- Save the compose file as docker-compose.yml (github.com/getlago/lago/blob/117b821/docker-compose.yml)
Use the compose file shown above.
- Set the values the compose file needs (github.com/getlago/lago/blob/117b821/docker-compose.yml)
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.
- Start it (github.com/getlago/lago/blob/117b821/docker-compose.yml)
docker compose up -d - Open the app (github.com/getlago/lago/blob/117b821/docker-compose.yml)
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). (github.com/getlago/lago/blob/117b821/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. (github.com/getlago/lago/blob/117b821/docker-compose.yml, checked 2026-10-22)
Signals
- Open issues and pull requests: 33
- Last push: 2026-10-21 (0 days before this page was built)
- Latest release: v1.55.0 on 2026-10-07 (14 days before this page was built)