Configuration
Every setting for vBilling v0.2: environment variables, Helm chart values, and pricing setup in your billing backend.
Environment Variables
vBilling reads its configuration from environment variables; the Helm chart sets them from values.yaml. Credentials come from Kubernetes Secrets (see existingSecret in each section of the chart).
Destinations
| Variable | Description | Default |
ADAPTERS |
Comma-separated destinations usage fans out to: stripe, metronome, lago, webhook, noop. Each destination reads the ledger through its own cursor. |
lago |
ADAPTER |
Deprecated v0.1 single-adapter form, used only when ADAPTERS is empty. |
(none) |
Identity and region
| Variable | Description | Default |
REGION |
Region stamped on every event (for example ap-southeast-2). Run one vBilling per control plane cluster. |
default |
CLUSTER_NAME |
Name of this control plane cluster; used as the CloudEvents source. |
control-plane |
ZONE |
Default zone when nodes carry no topology.kubernetes.io/zone label. |
(none) |
REGION_FROM_NODE |
Use the node's topology.kubernetes.io/region label instead of REGION. |
false |
DEFAULT_TENANT_CLASS |
Tenant class stamped on events when a tenant cluster has none (public, enterprise, government, dev, ...). |
(none) |
TENANT_SOURCE |
cluster: one customer per tenant cluster. project: one per vCluster Platform project. The vbilling.vcluster.com/tenant label always wins. |
cluster |
PLATFORM_CLUSTER |
vCluster Platform cluster name of this control plane cluster, so Platform enrichment only applies to local tenant clusters. |
(none) |
WATCH_NAMESPACES |
Comma-separated namespaces to discover tenant clusters in. Empty watches all namespaces. |
(all) |
Ledger
| Variable | Description | Default |
DATA_DIR |
Directory for the ledger and adapter state. Keep it on a persistent volume. |
/var/lib/vbilling |
RETENTION |
How long closed ledger segments are kept once every destination has consumed them. |
168h |
Metering
| Variable | Description | Default |
COLLECTION_INTERVAL |
Metering window size, aligned to the wall clock. Must be 10s to 1h and divide an hour. |
60s |
RECONCILE_INTERVAL |
How often tenant clusters are rediscovered. |
30s |
MAX_BACKFILL |
Windows missed during downtime are backfilled up to this age. |
6h |
OFFBOARD_GRACE |
A tenant must have no tenant clusters for this long before destinations offboard it. |
1h |
CPU_MEMORY_BASIS |
usage (metrics-server), requests, or max of both. |
usage |
METER_CONTROL_PLANE |
Also bill the tenant cluster's own control plane pods and data PVCs (normally covered by instance hours). |
false |
METER_BY_NAMESPACE |
Add the namespace inside the tenant cluster as a dimension, for tenant-side chargeback. |
false |
GPU_RESOURCES |
Full-GPU resource names. MIG (nvidia.com/mig-*) and nvidia.com/gpu.shared are always detected. |
nvidia.com/gpu,amd.com/gpu |
SKU_LABEL |
Node label whose value is the SKU (for example vbilling.vcluster.com/sku). |
(none) |
CAPACITY_TYPE_LABEL |
Node label for on-demand, spot, preemptible or reserved; beats cloud-provider labels. |
vbilling.vcluster.com/capacity-type |
UNHEALTHY_NODE_TAINTS |
Taints that mark a node down: its GPU time is recorded as downtime, not billed. Add your GPU health taints. |
node.kubernetes.io/not-ready,node.kubernetes.io/unreachable |
VCLUSTER_NODE_LABEL |
Extra dedicated-node selector, key=%s where %s is the tenant cluster name. See Dedicated nodes. |
(none) |
CUSTOM_METRICS |
Operator metrics accepted by the ingest API, code:unit[:key|key],... |
(none) |
PROMETHEUS_URL |
Any Prometheus HTTP API (Prometheus, Thanos, Mimir, VictoriaMetrics). Enables egress and GPU utilization metrics. |
(none) |
EGRESS_QUERY |
PromQL template for egress bytes. Placeholders: {{namespace}}, {{vcluster}}, {{instance}}, {{project}}, {{tenant}}, {{tenant_cluster}}, {{window}} (e.g. 60s), {{window_seconds}} (e.g. 60). |
container_network_transmit_bytes_total increase |
GPU_UTIL_QUERY, GPU_COUNT_QUERY |
Replace the GPU utilization queries (same placeholders, results grouped by modelName or gpu_type). none disables utilization. |
DCGM, namespace or exported_namespace |
PROMETHEUS_HEADERS |
Extra query headers, Name=value,..., e.g. X-Scope-OrgID for Mimir or Authorization. Basic auth also works in the URL. |
(none) |
PROMETHEUS_PRESET |
vcluster-platform reads vCluster Platform fleet observability: egress and GPU utilization selected by the vcluster_platform_instance and vcluster_platform_project labels. |
(none) |
PROMETHEUS_METRICS_FILE |
Metrics defined by PromQL, evaluated per tenant cluster at every window end (billable ones are created in the billing backends). |
(none) |
PROMETHEUS_BEARER_TOKEN_FILE |
Bearer token file, re-read on every query (for example a service account token for an OpenShift Thanos querier). |
(none) |
Tenant clusters with their own nodes
| Variable | Description | Default |
TENANT_API | Meter tenant clusters with their own nodes (Private Nodes, Auto Nodes, Standalone) through their own API. | true |
TENANT_KUBECONFIG_SECRET | Secret in each tenant cluster's namespace holding a read-only kubeconfig (vCluster exportKubeConfig.additionalSecrets). | vbilling-kubeconfig |
TENANT_ADMIN_FALLBACK | Also accept the admin kubeconfig vc-<name> (needs Secret read access). | false |
TENANT_CLUSTERS_FILE | YAML list of external tenant clusters: name, kubeconfig, tenant, displayName, project, metadata. | (none) |
PRIVATE_NODE_BILLING | node bills each private node whole; usage bills the pods on them. | node |
TENANT_EXCLUDE_NAMESPACES | Usage mode: namespaces never billed. | kube-system |
WATCH_DELETIONS | Bill pods and nodes deleted between windows until the moment they were deleted. | true |
DRA_GPU_DRIVERS | DRA drivers whose devices are GPUs (ResourceClaim metering). | gpu.nvidia.com,gpu.amd.com |
API and enforcement
| Variable | Description | Default |
LISTEN_ADDR |
HTTP listen address for the API, dashboard, metrics and webhooks. |
:8080 |
API_TOKEN |
Bearer token for /api/*. Empty leaves the API unauthenticated (keep the Service internal). |
(none) |
INGEST_TOKEN |
Bearer token for POST /api/v1/events; defaults to API_TOKEN. Ingest is disabled without a token. |
(none) |
ENFORCEMENT_MODE |
observe, annotate or enforce. See the README. |
observe |
ENFORCEMENT_RULES |
Overrides, event.type=state,... (for example alerts.spend_threshold_reached=suspended). |
(none) |
Stripe
| Variable | Description | Default |
STRIPE_API_KEY |
Secret or restricted key. Test-mode keys default to 20 requests/s. |
(none) |
STRIPE_WEBHOOK_SECRET |
Enables /webhooks/stripe (dunning and usage alerts). |
(none) |
STRIPE_SPLIT_METERS_BY |
Event fields that get their own meter (Stripe meters cannot price on dimensions). |
sku |
STRIPE_AUTO_SUBSCRIBE |
Subscribe tenants to active metered prices tagged metadata[vbilling_plan]=<plan>. |
false |
STRIPE_MAX_RPS |
Request rate cap; 0 picks 20 (test) or 200 (live). |
0 |
STRIPE_CANCEL_ON_REMOVE |
Cancel the tenant's subscription at period end when it is offboarded. |
false |
STRIPE_API_VERSION |
Pinned Stripe API version. |
2026-09-30.endive |
STRIPE_API_BASE |
API base URL (stripe-mock for tests). |
https://api.stripe.com |
Metronome
| Variable | Description | Default |
METRONOME_API_TOKEN |
Bearer token. |
(none) |
METRONOME_WEBHOOK_SECRET |
Enables /webhooks/metronome (spend, usage and credit alerts). |
(none) |
METRONOME_RATE_CARD |
Rate card alias or ID; creates a contract per tenant. A tenant's plan annotation overrides it. |
(none) |
METRONOME_STRIPE_LINK |
Create each tenant's Stripe customer and link it for invoicing (needs STRIPE_API_KEY). |
false |
METRONOME_STRIPE_COLLECTION_METHOD |
charge_automatically or send_invoice. |
charge_automatically |
METRONOME_API_BASE |
API base URL. |
https://api.metronome.com |
Lago
| Variable | Description | Default |
LAGO_API_URL |
Lago API base URL. |
http://localhost:3000 |
LAGO_API_KEY |
Required when the lago adapter is enabled. |
(none) |
DEFAULT_PLAN_CODE |
Plan code for new tenants (Lago plan; Stripe vbilling_plan tag). |
vcluster-standard |
BILLING_CURRENCY |
ISO 4217 currency for customers and plans. |
USD |
Webhook
| Variable | Description | Default |
WEBHOOK_URL |
Endpoint receiving CloudEvents 1.0 batches. |
(none) |
WEBHOOK_SECRET |
HMAC secret for the X-VBilling-Signature header (Stripe-compatible scheme). |
(none) |
WEBHOOK_HEADERS |
Extra headers, k=v,k=v. |
(none) |
Deprecated
| Variable | Description | Default |
SPOT_DISCOUNT_PERCENT |
Ignored since v0.2. Price capacity_type in your billing backend instead, so quantities stay physical and reconcilable. |
(none) |
Helm Chart Values
The chart deploys a StatefulSet with a persistent ledger volume, a Service, RBAC (enforcement verbs only outside observe mode), and optionally a ValidatingAdmissionPolicy and ServiceMonitor.
| Value | Sets | Default |
adapters |
ADAPTERS |
[lago] |
region / clusterName |
REGION / CLUSTER_NAME |
default / control-plane |
tenantClass / tenantSource |
DEFAULT_TENANT_CLASS / TENANT_SOURCE |
"" / cluster |
billing.collectionInterval / reconcileInterval / maxBackfill / offboardGrace |
COLLECTION_INTERVAL, RECONCILE_INTERVAL, MAX_BACKFILL, OFFBOARD_GRACE |
60s / 30s / 6h / 1h |
billing.defaultPlanCode / currency |
DEFAULT_PLAN_CODE / BILLING_CURRENCY |
vcluster-standard / USD |
metering.* |
CPU_MEMORY_BASIS, METER_*, GPU_RESOURCES, SKU_LABEL, CAPACITY_TYPE_LABEL, UNHEALTHY_NODE_TAINTS, VCLUSTER_NODE_LABEL, REGION_FROM_NODE, CUSTOM_METRICS |
see above |
prometheus.url / egressQuery / gpuUtilQuery / gpuCountQuery / headers / existingSecret / bearerTokenFile / preset / metrics |
PROMETHEUS_URL / EGRESS_QUERY / GPU_UTIL_QUERY / GPU_COUNT_QUERY / PROMETHEUS_HEADERS (chart Secret) / PROMETHEUS_BEARER_TOKEN_FILE / PROMETHEUS_PRESET / PROMETHEUS_METRICS_FILE (ConfigMap) |
(none) |
tenantAPI.enabled / secretName / adminFallback / privateNodeBilling / excludeNamespaces / externalClustersSecret |
TENANT_API / TENANT_KUBECONFIG_SECRET / TENANT_ADMIN_FALLBACK / PRIVATE_NODE_BILLING / TENANT_EXCLUDE_NAMESPACES / TENANT_CLUSTERS_FILE (Secret mounted at /etc/vbilling/tenants) |
true / vbilling-kubeconfig / false / node / [kube-system] / "" |
metering.watchDeletions / draGPUDrivers |
WATCH_DELETIONS / DRA_GPU_DRIVERS |
true / [gpu.nvidia.com, gpu.amd.com] |
platform.cluster |
PLATFORM_CLUSTER |
(none) |
lago.apiURL / apiKey / existingSecret (key api-key) |
LAGO_API_URL / LAGO_API_KEY |
(none) |
stripe.apiKey / webhookSecret / existingSecret (keys api-key, webhook-secret) |
STRIPE_API_KEY / STRIPE_WEBHOOK_SECRET |
(none) |
stripe.splitMetersBy / autoSubscribe / maxRPS / cancelOnRemove / apiVersion / apiBase |
STRIPE_* |
[sku] / false / 0 / false |
metronome.apiToken / webhookSecret / existingSecret (keys api-token, webhook-secret) |
METRONOME_API_TOKEN / METRONOME_WEBHOOK_SECRET |
(none) |
metronome.rateCard / stripeLink / stripeCollectionMethod / apiBase |
METRONOME_* |
(none) |
webhook.url / secret / headers / existingSecret (key secret) |
WEBHOOK_* |
(none) |
api.port / token / ingestToken / existingSecret (keys api-token, ingest-token) |
LISTEN_ADDR / API_TOKEN / INGEST_TOKEN |
8080 |
api.service.type / port |
Service |
ClusterIP / 8080 |
enforcement.mode / rules |
ENFORCEMENT_MODE / ENFORCEMENT_RULES |
observe |
enforcement.admissionPolicy.enabled |
ValidatingAdmissionPolicy blocking new pods in suspended namespaces (Kubernetes 1.30+) |
false |
persistence.enabled / size / storageClass / retention |
Ledger PVC (StatefulSet volumeClaimTemplate) / RETENTION |
true / 5Gi / "" / 168h |
serviceMonitor.enabled / interval |
Prometheus Operator ServiceMonitor |
false / 30s |
image.repository / tag / pullPolicy |
Container image |
ghcr.io/vclusterlabs-experiments/vbilling / v0.2.0 |
Example Helm install
helm upgrade --install vbilling deploy/helm/vbilling \
-n vbilling-system --create-namespace \
--set adapters='{metronome,webhook}' \
--set metronome.existingSecret=metronome-credentials \
--set metronome.rateCard=payg-aud --set metronome.stripeLink=true \
--set stripe.existingSecret=stripe-credentials \
--set webhook.url=https://data.example.com/usage \
--set region=ap-southeast-2 --set api.existingSecret=vbilling-api
Lago Pricing Configuration
vBilling bootstraps 9 billable metrics and a vcluster-standard plan on startup. All charges default to $0.00 per unit. You must configure pricing in the Lago UI or API to actually generate non-zero invoices.
Charges to configure
In the Lago UI, go to Plans > vCluster Standard > Edit and set the per-unit price for each charge:
| Metric Code | Aggregation Field | Charge Model | Suggested Price |
vcluster_cpu_core_hours |
cpu_core_hours |
Standard (per unit) |
$0.05 |
vcluster_memory_gb_hours |
memory_gb_hours |
Standard (per unit) |
$0.01 |
vcluster_storage_gb_hours |
storage_gb_hours |
Standard (per unit) |
$0.001 |
vcluster_instance_hours |
instance_hours |
Standard (per unit) |
$0.10 |
vcluster_gpu_hours |
gpu_hours |
Standard (per unit) |
$2.50 |
vcluster_gpu_utilization |
gpu_util_score |
Standard (per unit) |
$0.00 (informational) |
vcluster_network_egress_gb |
egress_gb |
Standard (per unit) |
$0.05 |
vcluster_lb_hours |
lb_hours |
Standard (per unit) |
$0.025 |
vcluster_private_node_hours |
private_node_hours |
Standard (per unit) |
$1.00 |
Example: AI Cloud pricing
For a GPU-focused AI Cloud selling compute to AI teams, you might set higher GPU prices and include a premium for dedicated infrastructure:
CPU Core-Hours: $0.08
Memory GB-Hours: $0.015
Storage GB-Hours: $0.002
Instance Hours: $0.25
GPU Hours (default): $3.50
GPU Hours (H100): $5.50
Network Egress/GB: $0.08
LoadBalancer Hours: $0.05
Private Node Hours: $2.00
Example: Internal platform team pricing
For an IDP (Internal Developer Platform) team doing internal chargebacks, use lower prices that reflect shared infrastructure costs:
CPU Core-Hours: $0.02
Memory GB-Hours: $0.005
Storage GB-Hours: $0.0005
Instance Hours: $0.05
GPU Hours (default): $1.00
Network Egress/GB: $0.01
LoadBalancer Hours: $0.01
Private Node Hours: $0.50
!
If you see usage events in Lago but invoices show $0.00, it means the charge amounts are still set to their defaults ($0). Edit the plan charges in the Lago UI.
To price H100 hours differently from L40S hours in Lago, add charge filters on the sku (or gpu_type) event property. vBilling sends every dimension as an event property: sku, region, capacity_type, billing_mode, tenant_class and tenant_cluster. Spot and preemptible discounts are a filter on capacity_type.
Stripe and Metronome Pricing
Stripe: vBilling creates a Billing Meter per metric and, because Stripe meters cannot price on dimensions, a meter per SKU as new SKUs appear (for example vcluster_gpu_hours__nvidia_h100_80gb_hbm3). Create a metered price on each meter you charge for. To subscribe new tenants automatically, tag the prices metadata[vbilling_plan]=vcluster-standard (or the tenant's plan annotation) and enable stripe.autoSubscribe.
Metronome: vBilling creates SUM billable metrics named <Metric> [<code>], each with one compound group key: region, sku, capacity_type, billing_mode, tenant_cluster for GPU hours. On your rate card, add a usage product per metric with pricing_group_key set to the dimensions you price on (for example [region, sku, capacity_type]) and presentation_group_key: [tenant_cluster] for invoice lines per tenant cluster. Rate cards carry the currency, so use one rate card per currency (for example AUD and NZD). Point metronome.rateCard or a tenant's plan annotation at it.