Lago Helm Chart

August 18, 2026 ยท View on GitHub

This Helm chart deploys the Lago billing system with optional dependencies on MinIO. Below are details about configuring the chart for different environments.

Prerequisites

  • Kubernetes 1.19+
  • Helm 3.5+
  • Persistent storage provisioner enabled in the cluster
  • Optionally: Minio service for staging environments

:warning: Please note that we strongly recommend to use managed PostgreSQL, Redis and S3 in a production environment.

Installation

To install the chart with the release name my-lago-release:

helm install my-lago-release .

You can customize the installation by overriding values in values.yaml with your own. The full list of configurable parameters can be found in the following sections.

Sample Command

helm install my-lago-release . \
  --set apiUrl=mydomain.dev \
  --set frontUrl=mydomain.dev

Development Setup

You can use the provided Taskfile.yaml to easily set up a local development environment with Task.

Prerequisites

Use Taskfile

  • List all task: task --list
  • Run end to end tests: task e2e
  • Run lint: task lint
  • ...

Configuration

Global Parameters

ParameterDescriptionDefault
global.licenseLago Premium License key""
global.databaseUrlPostgreSQL connection string, should follow this format: postgresql://USER:PASSWORD@HOST:PORT/DB""
global.redisUrlRedis connection string, should follow this format: redis://... or redis+sentinel://...""
global.existingSecretName of the secret containing sensitive values (database URL, Redis URL, AWS keys, SMTP credentials)""
global.s3.enabledEnable S3 storage for file uploadsfalse
global.s3.accessKeyIdAWS S3 access key ID (not required if using existing secret)""
global.s3.secretAccessKeyAWS S3 secret access key (not required if using existing secret)""
global.s3.bucketAWS S3 bucket name""
global.smtp.enabledEnable SMTP configuration for email sendingfalse
global.signup.enabledEnable or disable Lago's signup featuretrue
global.googleAuth.enabledEnable or disable logging through Google Authtrue
global.ingress.enabledEnable ingress resources for the applicationfalse
global.kubectl.imageRegistryDocker registry with kubectl image (for init containers)docker.io
global.kubectl.imageRepositoryDocker repository with kubectl image (for init containers)rancher/kubectl
global.kubectl.imageTagTag of kubectl Docker image (for init containers) - if unset (default) it is set to k8s cluster version""
global.clickhouse.enabledEnable ClickHouse integration within Lagofalse
global.clickhouse.kafka.tlsEnable TLS support for Kafka when using ClickHousetrue
global.clickhouse.kafka.saslMechanismsKafka SASL mechanism"SCRAM-SHA-512"
global.clickhouse.kafka.securityProtocolKafka Security protocol"SASL_SSL"
global.clickhouse.kafka.consumerGroupKafka consumer group name"events_consumer"
global.clickhouse.kafka.boostrapServers(Required) Kafka list of endpoints[]
global.clickhouse.kafka.topicsDictionary of Kafka topics{...}

Frontend Configuration

ParameterDescriptionDefault
front.tolerationsPod tolerations for Frontend pods[]
front.nodeSelectorNode selector for Frontend pods{}
front.affinityAffinity rules for Frontend pods{}
front.replicasNumber of frontend replicas1
front.service.portFrontend service port80
front.resources.requests.memoryMemory request for the frontend512Mi
front.resources.requests.cpuCPU request for the frontend200m
front.podAnnotationsAnnotations to add to the frontend pod{}
front.podLabelsLabels to add to the frontend pod{}

API Configuration

ParameterDescriptionDefault
api.tolerationsPod tolerations for API pods[]
api.nodeSelectorNode selector for API pods{}
api.affinityAffinity rules for API pods{}
api.replicasNumber of API replicas1
api.service.portAPI service port3000
api.rails.maxThreadsMaximum number of threads for the Rails app10
api.rails.webConcurrencyWeb concurrency setting for Rails4
api.rails.envRails environmentproduction
api.rails.logStdoutEnable or disable logging to stdouttrue
api.rails.logLevelLog level for the Rails apperror
api.sidekiqWeb.enabledEnable or disable Sidekiq Webtrue
api.resources.requests.memoryMemory request for the API1Gi
api.resources.requests.cpuCPU request for the API1000m
api.volumes.storageClassNameStorage class name API's persistent storage""
api.volumes.accessModesAccess mode for the API's persistent storageReadWriteOnce
api.volumes.storageStorage size for the API's persistent volume claim10Gi
api.podAnnotationsAnnotations to add to the API pod{}
api.podLabelsLabels to add to the API pod{}
api.livenessProbe.enabledEnable or disable liveness probetrue
api.livenessProbe.httpPathHTTP path for liveness probe/health
api.livenessProbe.httpPortHTTP port for liveness probe3000
api.livenessProbe.initialDelaySecondsLiveness probe initial delay0
api.livenessProbe.periodSecondsLiveness probe period10
api.livenessProbe.timeoutSecondsLiveness probe timeout1
api.livenessProbe.failureThresholdLiveness probe failure threshold3

Worker Configuration

ParameterDescriptionDefault
worker.tolerationsPod tolerations for Worker pods[]
worker.nodeSelectorNode selector for Worker pods{}
worker.affinityAffinity rules for Worker pods{}
worker.replicasNumber of Worker replicas1
worker.rails.sidekiqConcurrencySidekiq concurrency for Worker100
worker.rails.envWorker environmentproduction
worker.rails.logStdoutEnable or disable logging to stdouttrue
worker.rails.logLevelLog level for Workererror
worker.resources.requests.memoryMemory request for Worker1Gi
worker.resources.requests.cpuCPU request for Worker1000m
worker.livenessProbe.enabledEnable or disable liveness probetrue
worker.livenessProbe.httpPathHTTP path for liveness probe/
worker.livenessProbe.httpPortHTTP port for liveness probe8080
worker.livenessProbe.initialDelaySecondsLiveness probe initial delay0
worker.livenessProbe.periodSecondsLiveness probe period10
worker.livenessProbe.timeoutSecondsLiveness probe timeout1
worker.livenessProbe.failureThresholdLiveness probe failure threshold3

Events Consumer Configuration

ParameterDescriptionDefault
eventsConsumer.databasePoolSize of the database connection pool10
eventsConsumer.tolerationsPod tolerations for Events Consumer pods[]
eventsConsumer.nodeSelectorNode selector for Events Consumer pods{}
eventsConsumer.affinityAffinity rules for Events Consumer pods{}
eventsConsumer.rails.envEvents Consumer environmentproduction
eventsConsumer.rails.logStdoutEnable or disable logging to stdouttrue
eventsConsumer.rails.logLevelLog level for the Rails apperror
eventsConsumer.resources.requests.memoryMemory request for Events Consumer1Gi
eventsConsumer.resources.requests.cpuCPU request for Events Consumer1100m
eventsConsumer.podAnnotationsAnnotations to add to the Events Consumer pod{}
eventsConsumer.podLabelsLabels to add to the Events Consumer pod{}
eventsConsumer.extraEnvAdditional environment variables for pods{}

Events Processor Configuration

ParameterDescriptionDefault
eventsProcessor.databasePoolSize of the database connection pool10
eventsProcessor.envEvents Processor environmentproduction
eventsProcessor.tolerationsPod tolerations for Events Processor pods[]
eventsProcessor.nodeSelectorNode selector for Events Processor pods{}
eventsProcessor.affinityAffinity rules for Events Processor pods{}
eventsProcessor.resources.requests.memoryMemory request for Events Processor1Gi
eventsProcessor.resources.requests.cpuCPU request for Events Processor1100m
eventsProcessor.podAnnotationsAnnotations to add to the Events Processor pod{}
eventsProcessor.podLabelsLabels to add to the Events Processor pod{}
eventsProcessor.extraEnvAdditional environment variables for pods{}

Events Worker Configuration

ParameterDescriptionDefault
eventsWorker.tolerationsPod tolerations for Events Worker pods[]
eventsWorker.nodeSelectorNode selector for Events Worker pods{}
eventsWorker.affinityAffinity rules for Events Worker pods{}
eventsWorker.replicasNumber of Events Worker replicas1
eventsWorker.rails.sidekiqConcurrencySidekiq concurrency for Events Worker100
eventsWorker.rails.envEvents Worker environmentproduction
eventsWorker.resources.requests.memoryMemory request for Events Worker1Gi
eventsWorker.resources.requests.cpuCPU request for Events Worker1000m
eventsWorker.livenessProbe.enabledEnable or disable liveness probetrue
eventsWorker.livenessProbe.httpPathHTTP path for liveness probe/
eventsWorker.livenessProbe.httpPortHTTP port for liveness probe8080
eventsWorker.livenessProbe.initialDelaySecondsLiveness probe initial delay0
eventsWorker.livenessProbe.periodSecondsLiveness probe period10
eventsWorker.livenessProbe.timeoutSecondsLiveness probe timeout1
eventsWorker.livenessProbe.failureThresholdLiveness probe failure threshold3

Clock Worker Configuration

ParameterDescriptionDefault
clockWorker.tolerationsPod tolerations for Clock Worker pods[]
clockWorker.nodeSelectorNode selector for Clock Worker pods{}
clockWorker.affinityAffinity rules for Clock Worker pods{}
clockWorker.replicasNumber of Clock Worker replicas1
clockWorker.rails.sidekiqConcurrencySidekiq concurrency for Clock Worker100
clockWorker.rails.envClock Worker environmentproduction
clockWorker.resources.requests.memoryMemory request for Clock Worker1Gi
clockWorker.resources.requests.cpuCPU request for Clock Worker1000m
clockWorker.livenessProbe.enabledEnable or disable liveness probetrue
clockWorker.livenessProbe.httpPathHTTP path for liveness probe/
clockWorker.livenessProbe.httpPortHTTP port for liveness probe8080
clockWorker.livenessProbe.initialDelaySecondsLiveness probe initial delay0
clockWorker.livenessProbe.periodSecondsLiveness probe period10
clockWorker.livenessProbe.timeoutSecondsLiveness probe timeout1
clockWorker.livenessProbe.failureThresholdLiveness probe failure threshold3

Billing Worker Configuration

ParameterDescriptionDefault
billingWorker.tolerationsPod tolerations for Billing Worker pods[]
billingWorker.nodeSelectorNode selector for Billing Worker pods{}
billingWorker.affinityAffinity rules for Billing Worker pods{}
billingWorker.replicasNumber of Billing Worker replicas1
billingWorker.rails.sidekiqConcurrencySidekiq concurrency for Billing Worker100
billingWorker.rails.envBilling Worker environmentproduction
billingWorker.resources.requests.memoryMemory request for Billing Worker1Gi
billingWorker.resources.requests.cpuCPU request for Billing Worker1000m
billingWorker.livenessProbe.enabledEnable or disable liveness probetrue
billingWorker.livenessProbe.httpPathHTTP path for liveness probe/
billingWorker.livenessProbe.httpPortHTTP port for liveness probe8080
billingWorker.livenessProbe.initialDelaySecondsLiveness probe initial delay0
billingWorker.livenessProbe.periodSecondsLiveness probe period10
billingWorker.livenessProbe.timeoutSecondsLiveness probe timeout1
billingWorker.livenessProbe.failureThresholdLiveness probe failure threshold3

PDF Worker Configuration

ParameterDescriptionDefault
pdfWorker.tolerationsPod tolerations for PDF Worker pods[]
pdfWorker.nodeSelectorNode selector for PDF Worker pods{}
pdfWorker.affinityAffinity rules for PDF Worker pods{}
pdfWorker.replicasNumber of PDF Worker replicas1
pdfWorker.rails.sidekiqConcurrencySidekiq concurrency for PDF Worker100
pdfWorker.rails.envPDF Worker environmentproduction
pdfWorker.resources.requests.memoryMemory request for PDF Worker1Gi
pdfWorker.resources.requests.cpuCPU request for PDF Worker1000m
pdfWorker.livenessProbe.enabledEnable or disable liveness probetrue
pdfWorker.livenessProbe.httpPathHTTP path for liveness probe/
pdfWorker.livenessProbe.httpPortHTTP port for liveness probe8080
pdfWorker.livenessProbe.initialDelaySecondsLiveness probe initial delay0
pdfWorker.livenessProbe.periodSecondsLiveness probe period10
pdfWorker.livenessProbe.timeoutSecondsLiveness probe timeout1
pdfWorker.livenessProbe.failureThresholdLiveness probe failure threshold3

Webhook Worker Configuration

ParameterDescriptionDefault
webhookWorker.tolerationsPod tolerations for Webhook Worker pods[]
webhookWorker.nodeSelectorNode selector for Webhook Worker pods{}
webhookWorker.affinityAffinity rules for Webhook Worker pods{}
webhookWorker.replicasNumber of Webhook Worker replicas1
webhookWorker.rails.sidekiqConcurrencySidekiq concurrency for Webhook Worker100
webhookWorker.rails.envWebhook Worker environmentproduction
webhookWorker.resources.requests.memoryMemory request for Webhook Worker1Gi
webhookWorker.resources.requests.cpuCPU request for Webhook Worker1000m
webhookWorker.livenessProbe.enabledEnable or disable liveness probetrue
webhookWorker.livenessProbe.httpPathHTTP path for liveness probe/
webhookWorker.livenessProbe.httpPortHTTP port for liveness probe8080
webhookWorker.livenessProbe.initialDelaySecondsLiveness probe initial delay0
webhookWorker.livenessProbe.periodSecondsLiveness probe period10
webhookWorker.livenessProbe.timeoutSecondsLiveness probe timeout1
webhookWorker.livenessProbe.failureThresholdLiveness probe failure threshold3

Payment Worker Configuration

ParameterDescriptionDefault
paymentWorker.tolerationsPod tolerations for Payment Worker pods[]
paymentWorker.nodeSelectorNode selector for Payment Worker pods{}
paymentWorker.affinityAffinity rules for Payment Worker pods{}
paymentWorker.replicasNumber of Payment Worker replicas1
paymentWorker.rails.sidekiqConcurrencySidekiq concurrency for Payment Worker100
paymentWorker.rails.envPayment Worker environmentproduction
paymentWorker.resources.requests.memoryMemory request for Payment Worker1Gi
paymentWorker.resources.requests.cpuCPU request for Payment Worker1000m
paymentWorker.livenessProbe.enabledEnable or disable liveness probetrue
paymentWorker.livenessProbe.httpPathHTTP path for liveness probe/
paymentWorker.livenessProbe.httpPortHTTP port for liveness probe8080
paymentWorker.livenessProbe.initialDelaySecondsLiveness probe initial delay0
paymentWorker.livenessProbe.periodSecondsLiveness probe period10
paymentWorker.livenessProbe.timeoutSecondsLiveness probe timeout1
paymentWorker.livenessProbe.failureThresholdLiveness probe failure threshold3

Note on Readiness Probes: Currently, readiness probes are not explicitly configured for any of the components in this Helm chart.

Migration Job Configuration

The database migration runs as a pre-install/pre-upgrade Helm hook Job. Use these to keep it schedulable on clusters with tainted or dedicated nodes.

ParameterDescriptionDefault
job.migrate.tolerationsPod tolerations for the migration Job[]
job.migrate.nodeSelectorNode selector for the migration Job{}
job.migrate.affinityAffinity rules for the migration Job{}
job.migrate.resourcesResource requests/limits for the migration Job{}
job.migrate.extraEnvExtra environment variables for the migration Job{}
job.migrate.loadSchemaLoad the schema (db:schema:load) instead of running migrationsfalse

MinIO Configuration

ParameterDescriptionDefault
minio.enabledEnable MinIO for object storagetrue
minio.replicasNumber of MinIO replicas2
minio.persistence.sizePersistent volume size for MinIO10Gi
minio.ingress.enabledEnable ingress for MinIOtrue
minio.ingress.hostsHostnames for MinIO ingressminio.lago.dev
minio.ingress.ingressClassNameSpecify the ingress class name for MinIO ingressnginx
minio.ingress.pathPath for the MinIO ingress/
minio.bucketsList of S3 buckets to create on MinIO[]
minio.buckets[].nameName of the bucket (should match the release name if using fullnameOverride)my-lago-minio
minio.buckets[].policyAccess policy for the bucket (none, readonly, writeonly, readwrite)none
minio.buckets[].purgeIf true, purges the bucket upon deletionfalse
minio.buckets[].versioningEnable versioning for the bucketfalse
minio.buckets[].objectlockingEnable object locking for the bucketfalse
minio.fullnameOverrideOverride the full name for MinIO resources (should match the release name)""
minio.nameOverrideOverride the short name for MinIO resourcesminio
minio.endpointEndpoint URL for accessing MinIO""

MinIO Configuration Notes

When deploying MinIO with this Helm chart, it is recommended to align the fullnameOverride and the name of the first bucket with the release name to ensure proper resource naming and organization.

Example:

If you are installing the release with the name old-lago:

helm install old-lago ./lago-helm-charts --values value_old.yaml

Your values.yaml for MinIO should include:

minio:
  enabled: true
  fullnameOverride: "old-lago-minio"  # Matches the release name
  buckets:
    - name: "old-lago-minio"          # Matches the fullnameOverride
      policy: none
      purge: false
      versioning: false
      objectlocking: false
  endpoint: "http://minio.yourdomain.tld"

Key Points

  • fullnameOverride: This parameter allows you to set a custom name for MinIO resources. For better traceability, align it with the release name.
  • First Bucket Name: The first bucket in the buckets list should match the fullnameOverride to ensure consistent bucket naming conventions.
  • Ingress Configuration: Make sure the hosts in the ingress section match your MinIO endpoint URL.

Secrets

The chart supports using an existing secret to store sensitive values such as database URLs, Redis URLs, AWS keys, and SMTP credentials. To use an existing secret, set the global.existingSecret parameter to the name of the secret. If you use an existing secret, the following keys are expected:

  • databaseUrl (required)
  • redisUrl (required)
  • awsS3AccessKeyId (optional)
  • awsS3SecretAccessKey (optional)
  • smtpUsername (optional)
  • smtpPassword (optional)
  • googleAuthClientId (optional)
  • googleAuthClientSecret (optionals)

If you want to provide an existing secret for the encryption keys, you can also set the encryption.existingSecret parameter to the name of the secret. The following keys are expected:

  • encryptionPrimaryKey (required)
  • encryptionDeterministicKey (required)
  • encryptionKeyDerivationSalt (required)

Storage Recommendation

We strongly recommend using either Amazon S3 alternatives for object storage when deploying Lago. These solutions provide reliable, scalable storage that can be accessed by multiple pods without encountering issues.

If neither S3 nor MinIO is configured, the system will default to using a Persistent Volume Claim (PVC). However, this approach is strongly discouraged as it can lead to issues such as multi-attach errors when volumes are accessed by more than one pod simultaneously. For this reason, it is important to configure S3 or MinIO to avoid potential complications with PVCs.

By opting for S3 or alternatives, you ensure better reliability and scalability for your deployment.

For additional customization, refer to the comments in values.yaml.

Uninstall

To uninstall/delete the my-lago-release:

helm delete my-lago-release