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:

i
vBilling auto-discovers tenant clusters by scanning for StatefulSets/Deployments with the 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 lago

Generate 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" >> .env

Start 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 running

Create 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>"
!
Keep this API key safe. You will need it when configuring vBilling. In production, store it in a Kubernetes Secret.

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: deployed

Or 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...
i
When running locally, vBilling uses your ~/.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

  1. Navigate to Plans in the left sidebar
  2. Click on vCluster Standard (code: vcluster-standard)
  3. Scroll to the Charges section
  4. For each charge, click the edit icon and set the price per unit:
i
Recommended pricing (adjust to your costs):
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 Lago

Check 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", # ... # } # }
✓
If you see customers and usage data, vBilling is working. Usage events are sent every collection interval (default: 60 seconds). Invoices are generated monthly by Lago.

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=false

Deploy 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-credentials

Verify

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-standard
✓
This is output from a verified end-to-end test. vBilling runs as a StatefulSet with a persistent ledger and an in-cluster ServiceAccount, discovers tenant clusters via RBAC, and delivers events to Lago. Check delivery with kubectl -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 -v

Next Steps