Getting Started
Go from zero to metered vCluster billing in four steps. This guide walks you through deploying Lago, installing vBilling, configuring pricing, and verifying the full pipeline.
Prerequisites
Before you begin, make sure you have the following tools installed:
- Docker and Docker Compose (for running Lago locally)
- kubectl configured to talk to a Kubernetes cluster
- vCluster CLI (
vcluster) with at least one vCluster running - Helm 3 (for the Helm installation path)
- Go 1.22+ (only if building from source)
- metrics-server installed in the cluster (for CPU/memory metrics)
app=vcluster label, or via the vCluster Platform API if available. Make sure you have at least one vCluster running before starting.1Deploy Lago
Lago is the open-source billing engine that vBilling sends usage events to. You can run it locally with Docker Compose or deploy it to your cluster.
Clone the Lago repository
git clone https://github.com/getlago/lago.git
cd lagoGenerate RSA keys for Lago API authentication
# Generate RSA keypair for JWT signing
openssl genrsa -out lago.key 2048
openssl rsa -in lago.key -pubout -out lago.pub
# Base64-encode for environment variables
export LAGO_RSA_PRIVATE_KEY=$(base64 -w0 lago.key 2>/dev/null || base64 lago.key)
echo "RSA_PRIVATE_KEY=$LAGO_RSA_PRIVATE_KEY" >> .envStart Lago with Docker Compose
docker compose up -d
# Wait for services to be healthy (takes ~60 seconds)
docker compose ps
# Expected output:
# NAME STATUS
# lago-api-1 running (healthy)
# lago-front-1 running
# lago-db-1 running (healthy)
# lago-redis-1 running (healthy)
# lago-worker-1 runningCreate an organization via GraphQL
Lago needs an organization before you can use the API. Run this GraphQL mutation against the Lago API:
curl -X POST http://localhost:3000/graphql \
-H "Content-Type: application/json" \
-d '{
"query": "mutation { registerUser(input: { email: \"admin@example.com\", password: \"ILoveLago99!\", organizationName: \"vBilling\" }) { token user { id email } } }"
}'
# Expected output:
# {"data":{"registerUser":{"token":"eyJhb...","user":{"id":"...","email":"admin@example.com"}}}}Get the API key from the database
The API key is stored in the Lago database. Extract it with:
# Connect to the Lago database and get the API key
docker compose exec db psql -U lago -d lago \
-c "SELECT value FROM api_keys LIMIT 1;"
# Expected output:
# value
# ---------------------------------
# a1b2c3d4-e5f6-7890-abcd-ef1234567890
# (1 row)
# Save it for later
export LAGO_API_KEY="<your-lago-api-key>"2Install vBilling
You can install vBilling via Helm (recommended for production) or run it locally during development.
Option A: Helm chart (recommended)
The chart defaults to the published image ghcr.io/vclusterlabs-experiments/vbilling:v0.2.0 (linux/amd64 and linux/arm64). Build your own only to run local changes:
# Build multi-arch image
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t <your-registry>/vbilling:dev \
--push .
# Or single-arch for local testing:
# docker build -t vbilling:latest .Install with Helm:
helm upgrade --install vbilling deploy/helm/vbilling \
--namespace vbilling-system --create-namespace \
--set lago.apiURL="http://lago-api.lago-system.svc.cluster.local:3000" \
--set lago.apiKey="$LAGO_API_KEY"
# Expected output:
# Release "vbilling" does not exist. Installing it now.
# NAME: vbilling
# NAMESPACE: vbilling-system
# STATUS: deployedOr use an existing Kubernetes secret for the API key:
# Create the secret first
kubectl create secret generic vbilling-lago-secret \
-n vbilling-system \
--from-literal=api-key="$LAGO_API_KEY"
# Install with secret reference
helm upgrade --install vbilling deploy/helm/vbilling \
--namespace vbilling-system --create-namespace \
--set lago.apiURL="http://lago-api.lago-system.svc.cluster.local:3000" \
--set lago.existingSecret="vbilling-lago-secret"Option B: Run locally (development)
# Clone the repository
git clone https://github.com/vClusterLabs-Experiments/vbilling.git
cd vbilling
# Build the binary
make build
# Expected output:
# CGO_ENABLED=0 go build -o bin/vbilling ./cmd/vbilling
# Run with environment variables
export LAGO_API_URL="http://localhost:3000"
export LAGO_API_KEY="<your-lago-api-key>"
./bin/vbilling
# Expected output:
# vBilling - vCluster Billing Controller
# =======================================
# Lago API: http://localhost:3000
# Plan: vcluster-standard | Currency: USD
# Collection: 1m0s | Reconcile: 30s
# Bootstrapping Lago billing configuration...
# [bootstrap] setting up Lago billing configuration...
# [bootstrap] created metric "vcluster_cpu_core_hours" (id=...)
# [bootstrap] created metric "vcluster_memory_gb_hours" (id=...)
# ...
# [bootstrap] created plan "vcluster-standard" (id=...) with 9 charges
# [bootstrap] Lago billing configuration complete
# Starting billing controller...~/.kube/config to connect to the cluster. Make sure your kubeconfig context points to the cluster with your tenant clusters.3Configure Pricing in Lago
vBilling bootstraps all 9 billable metrics and a default plan (vcluster-standard) on startup. However, all charges default to $0.00. You must set pricing in the Lago UI.
Open the Lago dashboard
# Lago UI is available at:
open http://localhost:80
# Log in with the credentials you used in the GraphQL mutation:
# Email: admin@example.com
# Password: ILoveLago99!Edit the plan charges
- Navigate to Plans in the left sidebar
- Click on vCluster Standard (code:
vcluster-standard) - Scroll to the Charges section
- For each charge, click the edit icon and set the price per unit:
CPU Core-Hours: $0.05 | Memory GB-Hours: $0.01 | Storage GB-Hours: $0.001
Instance Hours: $0.10 | GPU Hours: $2.50 (default) | Network Egress/GB: $0.05
LoadBalancer Hours: $0.025 | Private Node Hours: $1.00
4Verify It Works
Once vBilling is running, it will auto-discover your tenant clusters and start sending usage events within one reconcile cycle (default: 30 seconds).
Check vBilling logs
# If running via Helm:
kubectl logs -n vbilling-system -l app=vbilling -f
# Expected output:
# [controller] starting billing controller
# [controller] reconcile interval: 30s, collection interval: 1m0s
# [controller] new vCluster discovered: vcluster-team-alpha/my-vcluster
# [controller] ensured customer vcluster-vcluster-team-alpha-my-vcluster
# [controller] created subscription sub-vcluster-vcluster-team-alpha-my-vcluster -> plan vcluster-standard
# [metrics] vcluster-team-alpha: CPU=0.250 cores, Memory=0.51 GB
# [metrics] vcluster-team-alpha: Storage=10.00 GB (2 PVCs)
# [controller] sent 4 billing events to LagoCheck Lago for customers
Open the Lago UI and navigate to Customers. You should see one customer per discovered vCluster:
# Or verify via the API:
curl -s http://localhost:3000/api/v1/customers \
-H "Authorization: Bearer $LAGO_API_KEY" | jq '.customers[].external_id'
# Expected output:
# "vcluster-vcluster-team-alpha-my-vcluster"
# "vcluster-default-dev-cluster"Check current usage
curl -s http://localhost:3000/api/v1/customers/vcluster-vcluster-team-alpha-my-vcluster/current_usage\?external_subscription_id=sub-vcluster-vcluster-team-alpha-my-vcluster \
-H "Authorization: Bearer $LAGO_API_KEY" | jq
# Expected output:
# {
# "customer_usage": {
# "from_datetime": "2025-01-01T00:00:00Z",
# "to_datetime": "2025-01-31T23:59:59Z",
# "amount_cents": 1250,
# "amount_currency": "USD",
# ...
# }
# }Try It Locally with vind
You can test the full pipeline on your laptop using vind (vCluster in Docker) -- no cloud cluster needed.
Create a vind control plane cluster
# Set Docker driver and create a control plane cluster
vcluster use driver docker
vcluster create vbilling-host --connect=true
# Install metrics-server
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
kubectl patch deployment metrics-server -n kube-system --type=json \
-p='[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}]'Create nested tenant clusters
vcluster use driver helm
vcluster create team-alpha --namespace vcluster-team-alpha --connect=false
vcluster create team-beta --namespace vcluster-team-beta --connect=false
vcluster create team-gpu --namespace vcluster-team-gpu --connect=falseDeploy vBilling as a pod in the vind cluster
# Build the image
docker build --build-arg TARGETARCH=arm64 -t vbilling:test .
# Load into vind's containerd
docker save vbilling:test | docker exec -i vcluster.cp.vbilling-host \
ctr -n k8s.io images import --all-platforms -
# Create secret and install via Helm
kubectl create namespace vbilling-system
kubectl create secret generic lago-credentials \
--namespace vbilling-system \
--from-literal=api-key="$LAGO_API_KEY"
helm upgrade --install vbilling deploy/helm/vbilling \
--namespace vbilling-system \
--set image.repository=vbilling \
--set image.tag=test \
--set image.pullPolicy=Never \
--set lago.apiURL=http://host.docker.internal:3000 \
--set lago.existingSecret=lago-credentialsVerify
kubectl logs -n vbilling-system statefulset/vbilling
# Expected output (v0.2):
# Using in-cluster Kubernetes config
# [controller] starting (adapters=[lago], window=1m0s, reconcile=30s, region=default)
# API listening on :8080
# [bootstrap] setting up Lago billing configuration...
# [controller] window 10:18:20: 17 events for 3 tenant cluster(s) (11 pods, 5 GPU pods, 0 nodes down)
# [lago] subscribed tenant acme to plan vcluster-standardkubectl -n vbilling-system port-forward svc/vbilling 8080 and curl localhost:8080/api/v1/status.Clean up
vcluster use driver docker
vcluster delete vbilling-host
cd deploy/lago && docker compose down -vNext Steps
- Configuration -- tune intervals, namespaces, and pricing
- Metrics Reference -- understand all 9 billable metrics
- Dedicated and Private Nodes -- set up dedicated node billing
- Troubleshooting -- common issues and fixes