Skip to main content
Testkube 2.11.0 is out! Improved insights and metrics, more responsive AI chat, Gateway API support, and much more! Read More

Offline Usage Export

Generate an encrypted usage report from your on-prem Testkube Enterprise control plane when the online licensing path is not available. The export runs as a one-shot Kubernetes Job, reads your Enterprise database (MongoDB or PostgreSQL), and produces a .zip file containing encrypted usage data for license reporting.

Who this is for

This flow targets on-prem Enterprise installs with MongoDB or PostgreSQL. Hosted Testkube Cloud organizations use the online licensing path instead — see Commercial Licensing.

Prerequisites

Before you export usage, confirm the following:

  1. Install the Testkube CLI on your workstation.
  2. Install kubectl and helm — the export CLI command uses both.
  3. Configure kubectl for the cluster where Testkube Enterprise runs.
  4. Deploy Testkube Enterprise in the target namespace (the control plane API must be running).
  5. The export must use the same database connection, credentials master password, and enterprise license key as the control plane. When you use the CLI without a values file, these settings are auto-configured from your Enterprise deployment — you do not need to supply them manually.
  6. Ensure database hostnames resolve from inside the cluster (Kubernetes service names, not localhost or a laptop port-forward address). Auto-config reads the same in-cluster connection settings the control plane uses.
In-cluster database URLs

The export Job runs inside the cluster. Connection strings must use in-cluster service names (for example mongodb://testkube-enterprise-mongodb:27017), not localhost.

When Testkube Enterprise is already installed, run:

testkube pro export usage -n testkube --output ./plan-usage.zip

The command:

  1. Discovers database and license settings from your Enterprise deployment (when no values file is passed).
  2. Installs the standalone testkube-usage-export Helm chart as a one-shot Job.
  3. Waits for the Job to finish and downloads the zip to --output.
  4. Removes the Helm release automatically (unless you pass --keep-release).
  5. Prints next steps.

For custom or air-gapped installs, pass a values file and disable auto-discovery:

testkube pro export usage \
-n <namespace> \
-f <your-values.yaml> \
--no-auto-config \
--output ./plan-usage.zip

See Parameters and configuration for all CLI flags and Helm values.

Manual Helm workflow (optional)

If you prefer Helm directly:

helm repo add testkubeenterprise https://kubeshop.github.io/testkube-cloud-charts
helm repo update

helm upgrade --install testkube-usage-export testkubeenterprise/testkube-usage-export \
-n <namespace> --create-namespace \
-f <your-values.yaml>

Then:

  1. Watch Job logs until you see usage export complete:

    kubectl -n <namespace> logs -f -l app.kubernetes.io/component=usage-export -c usage-export
  2. Copy the zip while the pod is still Running (the Job keeps the pod alive briefly after export):

    kubectl -n <namespace> cp <pod>:/output/plan-usage-<name>-<timestamp>.zip ./plan-usage.zip
  3. Uninstall when finished:

    helm uninstall testkube-usage-export -n <namespace>

Parameters and configuration

Configuration rules

Read these before customizing values:

  1. One database backend — enable MongoDB or PostgreSQL, never both. Helm fails at render time if both or neither are enabled.
  2. Match Enterprise — database DSN, database name, credentials master password, and license key must match the running control plane. The CLI auto-configures these from the control-plane deployment by default; use a values file with --no-auto-config only when auto-discovery does not apply.
  3. In-cluster connectivity — DSNs and hostnames must resolve from inside the export Job pod.
  4. Postgres precedence — if both API_POSTGRES_URL and API_MONGO_DSN are set, the export connects to Postgres.
  5. License key required — the export cannot run without ENTERPRISE_LICENSE_KEY. An offline license file mount alone is not enough unless the key is also available as an environment variable or secret.
  6. Secrets over inline values — inline passwords and license keys in Helm values work for testing; use secretKeyRef in production.

CLI flags

Full reference for testkube pro export usage:

FlagTypeDefaultDescription
-n, --namespacestringtestkubeNamespace where Testkube Enterprise is installed and where the export Job runs
--contextstringcurrent kubeconfig contextOverride Kubernetes context
--releasestringtestkube-usage-exportHelm release name; also used to label and find the export Job
-f, --valuesstring arraynoneOne or more Helm values files. Passing any -f disables auto-config unless --auto-config is also set
--no-auto-configboolfalseRequire explicit -f values; do not read settings from the Enterprise deployment
--auto-configboolfalseForce auto-config even when -f is passed (discovered values are merged; CLI --helm-set wins on conflict)
--helm-setkey=value mapnoneExtra Helm --set overrides (for example usageExport.weeks=8, image.tag=2.12.0)
--helm-argkey=value mapnonePass raw Helm flags (for example timeout=30m becomes --timeout 30m)
--chart-versionstringlatest from repoPin the testkube-usage-export chart version
--chart-pathstringnoneLocal chart directory (air-gapped mirror); skips the remote chart repo
--outputstringbasename of remote zipLocal filesystem path for the downloaded export file
--timeoutduration15mMaximum time to wait for the Job pod and export completion
--create-namespacebooltrueCreate the namespace if missing; auto-config sets this to false
--keep-releaseboolfalseDo not run helm uninstall after download
--dry-runboolfalsePrint Helm and kubectl commands without creating resources

Auto-config behavior

When no -f is passed, the CLI:

  • Finds the testkube-cloud-api Deployment in the target namespace (by label, then by name).
  • Maps control-plane environment variables to Helm --set values:
    • Postgres: API_POSTGRES_URL (literal or secret ref), or component secrets (DATABASE_USERNAME, DATABASE_PASSWORD, DATABASE_HOST, DATABASE_NAME)
    • MongoDB (if Postgres is not configured): API_MONGO_DSN, API_MONGO_DB, API_MONGO_READ_PREFERENCE
    • Credentials: CREDENTIALS_MASTER_PASSWORD (secret ref preferred; inline values trigger a warning)
    • License: ENTERPRISE_LICENSE_KEY (secret ref preferred; inline values trigger a warning)
    • Custom CA: SSL_CERT_DIR and mounted CA secret (if present on the control plane)
  • Prints: Auto-configured usage export from deployment/<name>

Use --no-auto-config when:

  • Enterprise is in a different namespace than expected.
  • The control plane uses non-standard environment wiring the CLI cannot map.
  • The cluster is air-gapped and cannot reach the public chart repo (use --chart-path with a values file).
  • Multiple control-plane deployments exist in one namespace.

CLI override without a values file

testkube pro export usage -n testkube \
--helm-set usageExport.weeks=8 \
--helm-set image.tag=<your-enterprise-version>

Helm values reference

Chart and release

ValueDefaultDescription
nameOverride""Short name override for chart resources
fullnameOverride""Fully qualified resource name override

Image

ValueDefaultDescription
image.registrydocker.io (when empty)Container registry
image.repositorykubeshop/testkube-usage-exportImage repository
image.tagchart appVersionImage tag; should match your Enterprise release
image.tagSuffix""Appended to tag
image.digest""Pull by digest instead of tag
image.pullPolicyIfNotPresentKubernetes pull policy
imagePullSecrets[]Pull secrets for private registries

MongoDB (mongo.enabled: true)

ValueRequiredDefaultDescription
mongo.enabledyes (one backend)falseEnable MongoDB backend
mongo.dsnone of dsn / dsnSecretRef""Full MongoDB connection string
mongo.dsnSecretRefone of dsn / dsnSecretRef""Secret containing DSN; chart expects key MONGO_DSN
mongo.databasenotestkubecloudDatabase name (API_MONGO_DB)
mongo.readPreferencenosecondaryPreferredMongo read preference

PostgreSQL (postgres.enabled: true)

ValueRequiredDefaultDescription
postgres.enabledyes (one backend)falseEnable PostgreSQL backend
postgres.dsnone connection mode""Full API_POSTGRES_URL connection string
postgres.dsnSecretRefone connection mode""Secret name for full DSN
postgres.dsnSecretKeywith dsnSecretRefAPI_POSTGRES_URLKey inside DSN secret
postgres.secretRef.namecomponent mode""Secret with username, password, and endpoint keys
postgres.secretRef.usernameKeycomponent modeusernameUsername key in secret
postgres.secretRef.passwordKeycomponent modepasswordPassword key in secret
postgres.secretRef.endpointKeycomponent modeendpointHost/port key in secret
postgres.usernameif no usernameKey""Inline username (component mode)
postgres.endpointif no endpointKey""Inline host (component mode)
postgres.databasenotestkubecloudDatabase name
postgres.queryParamsno""Query string appended to the built URL (for example sslmode=require)

Component mode builds API_POSTGRES_URL inside the pod from separate environment variables — the same pattern as Enterprise.

Credentials and license

ValueRequiredDefaultDescription
credentials.masterPassword.valueone of value / secretKeyRef""Inline master password (not recommended for production)
credentials.masterPassword.secretKeyRef.nameone of value / secretKeyRef""Secret containing master password
credentials.masterPassword.secretKeyRef.keywith secretKeyRefpasswordKey inside secret
enterpriseLicenseKeyone of key / secretRef""Inline enterprise license key
enterpriseLicenseSecretRefone of key / secretRef""Secret name; chart reads key LICENSE_KEY

Export options

ValueDefaultDescription
usageExport.weeks4Weeks of usage history in the export
usageExport.output.mountPath/outputDirectory inside the pod where the zip is written

TLS / custom CA

For external databases signed by a private CA:

ValueDefaultDescription
customCaSecretRef""Secret containing CA certificate
customCaSecretKeyca.crtKey or filename within secret
customCaDirPath/etc/testkube/certsMount path; sets SSL_CERT_DIR in the container

Job and pod

ValueDefaultDescription
job.ttlSecondsAfterFinished86400Seconds before Kubernetes deletes the finished Job
job.backoffLimit0Job retries (0 = no retries)
job.resources{}CPU and memory limits for the export container
job.annotations{}Job metadata annotations
podAnnotations{}Pod annotations
nodeSelector{}Schedule the Job on specific nodes
tolerations[]Tolerations for tainted nodes
affinity{}Pod affinity or anti-affinity
serviceAccount.createtrueCreate a dedicated ServiceAccount
serviceAccount.nameautoOverride ServiceAccount name
serviceAccount.automountServiceAccountTokenfalseAPI token mount (not needed for export)

Container environment variables

When debugging with kubectl describe pod, these are the variables the export binary reads:

VariableSet byRequiredDescription
API_POSTGRES_URLpostgres valuesone of PG/MongoPostgreSQL connection string
API_MONGO_DSNmongo valuesone of PG/MongoMongoDB connection string
API_MONGO_DBmongo.databasewith MongoMongo database name
API_MONGO_READ_PREFERENCEmongo.readPreferencenoMongo read preference
CREDENTIALS_MASTER_PASSWORDcredentials valuesyesMaster password for encrypted database fields
ENTERPRISE_LICENSE_KEYlicense valuesyesLicense key; encrypts the export file
USAGE_EXPORT_WEEKSusageExport.weeksnoWeeks to export (default 4)
USAGE_EXPORT_OUTPUT_DIRusageExport.output.mountPathnoOutput directory (default /output)
SSL_CERT_DIRcustomCaSecretRefnoTrust custom CA for database TLS
POD_NAME / POD_NAMESPACEdownward APIautoEnables post-export keepalive for manual kubectl cp

Startup validation errors in pod logs:

Log / errorMeaning
API_POSTGRES_URL or API_MONGO_DSN is requiredNo database configured
ENTERPRISE_LICENSE_KEY is requiredLicense key missing
failed to connect to databaseDSN wrong or database unreachable from pod
no plans foundDatabase empty or wrong database name
failed to resolve canonical planPlan data missing

Example values

PostgreSQL via component secret (common Enterprise pattern):

postgres:
enabled: true
secretRef:
name: my-postgres-credentials
usernameKey: username
passwordKey: password
endpointKey: endpoint
database: testkubecloud
queryParams: sslmode=require

credentials:
masterPassword:
secretKeyRef:
name: my-credentials-master
key: password

enterpriseLicenseSecretRef: my-enterprise-license

usageExport:
weeks: 4

MongoDB via DSN secret:

mongo:
enabled: true
dsnSecretRef: my-mongo-credentials
database: testkubeEnterpriseDB
readPreference: secondaryPreferred

credentials:
masterPassword:
secretKeyRef:
name: my-credentials-master
key: password

enterpriseLicenseSecretRef: my-enterprise-license

Output file

  • Filename pattern: plan-usage-<plan-name>-<timestamp>.zip (written under /output in the pod).
  • Contents: encrypted usage data for license reporting (not human-readable CSV).
  • Handling: keep the file confidential; treat it like credential material.

Troubleshooting

General diagnostics

Replace <namespace> with your Enterprise namespace:

# Job status
kubectl -n <namespace> get jobs -l app.kubernetes.io/component=usage-export

# Pod logs (export binary output)
kubectl -n <namespace> logs -l app.kubernetes.io/component=usage-export -c usage-export

# Pod events
kubectl -n <namespace> describe pod -l app.kubernetes.io/component=usage-export

# Helm release
helm list -n <namespace> | grep usage-export

When the CLI command fails, it prints an Export logs section from the pod when available.

Successful export logs include:

  • usage export complete with outputPath, planId, weeksExported, and sizeBytes
  • Download: kubectl -n … cp … (for manual workflows)

CLI and auto-config errors

Message / symptomLikely causeFix
Testkube Enterprise cloud-api deployment not foundWrong -n, Enterprise not installed, or RBAC blocks kubectl get deployVerify kubectl get deploy -n <namespace> -l app.kubernetes.io/name=testkube-cloud-api; pass -f and --no-auto-config if Enterprise uses a non-standard layout
cloud-api has neither postgres nor mongo configurationAuto-config cannot find database env on control planeInspect control-plane deployment env; provide a values file with mongo.enabled or postgres.enabled
CREDENTIALS_MASTER_PASSWORD not found on cloud-api deploymentMaster password not exposed as env on control planeAdd to values file under credentials.masterPassword
ENTERPRISE_LICENSE_KEY not found on cloud-api deploymentLicense only in file mount, not envSet enterpriseLicenseKey or enterpriseLicenseSecretRef in values
offline license file installs require ENTERPRISE_LICENSE_KEY for usage exportENTERPRISE_LICENSE_FILE mount without key envProvide license key via Helm values (see Advanced Installation — offline license)
Could not derive usage-export config from cloud-apiPartial or unmapped control-plane envUse --no-auto-config with a complete values file
Helm command failedChart repo unreachable, bad version, or install timeoutAdd --chart-version; use --helm-arg timeout=30m; for air-gapped clusters, use --chart-path
Usage export Job not foundHelm install succeeded but Job not created yet, or wrong --releaseCheck helm status; verify release name matches Job labels
Invalid timeoutMalformed --timeoutUse duration syntax: 15m, 30m, 1h

Helm render and install errors

Message / symptomLikely causeFix
Enable exactly one database backendNeither mongo.enabled nor postgres.enabledSet exactly one to true
Enable only one database backend … not bothBoth backends enabledDisable one backend
Mongo configuration missing: provide mongo.dsnSecretRef or mongo.dsnMongo enabled without connectionAdd DSN or secret ref
Postgres configuration missing: provide postgres.secretRef.name, postgres.dsnSecretRef, or postgres.dsnPostgres enabled without connectionAdd one of the three Postgres connection modes
credentials.masterPassword is requiredMissing master password in valuesSet credentials.masterPassword.secretKeyRef or .value

Job runtime and export errors

Message / symptomLikely causeFix
invalid configuration: API_POSTGRES_URL or API_MONGO_DSN is requiredEnv not injected into podFix Helm values; re-run
invalid configuration: ENTERPRISE_LICENSE_KEY is requiredLicense not injectedSet enterpriseLicenseKey or enterpriseLicenseSecretRef
failed to connect to postgres / failed to connect to mongoWrong DSN, network policy, or TLSVerify in-cluster hostname; add customCaSecretRef for private CA; check NetworkPolicy
connect to mongo: … connection refused … localhost:27017DSN points to localhostReplace with in-cluster MongoDB service name
lookup <host>: no such hostWrong service name or database not in clusterAlign DSN with actual MongoDB or Postgres service (external MongoDB guide)
no plans foundWrong database name or fresh/empty installMatch mongo.database / postgres.database to Enterprise; confirm plan data exists
failed to resolve canonical planPlan record missing in databaseVerify Enterprise has completed initial setup
Job Failed, pod Error / CrashLoopBackOffBinary exited on validation or database errorRead pod logs (see General diagnostics)

Download and timeout errors

Message / symptomLikely causeFix
Timed out waiting for usage export podJob slow to schedule, image pull, or pendingkubectl describe job; check node capacity, image pull, and taints
Timed out waiting for usage exportExport or database query exceeded --timeoutIncrease --timeout; check database performance
Failed to stream pod logsRBAC or pod not readyVerify kubectl logs access; wait and retry
Usage export did not produce a downloadable zipJob failed before writing outputInspect pod logs for binary errors
Could not locate usage export zip in podExport failed or wrong output pathCheck /output in pod; verify usageExport.output.mountPath
kubectl cp / cannot exec into a container in a completed podManual copy after pod reached SucceededRe-run export; copy while Running, or use the CLI which streams logs and copies automatically
tar: executable file not found (manual cp only)Old image without tarUse a current testkube-usage-export image tag matching Enterprise
Post-export keepalive

The export pod stays alive for about 60 seconds after success to allow manual kubectl cp. The CLI copies as soon as usage export complete appears in logs.

Image and registry errors

Message / symptomLikely causeFix
ImagePullBackOff / ErrImagePullTag missing, wrong registry, or no pull accessPin image.tag to your Enterprise version; configure imagePullSecrets; mirror the image internally
Wrong or incomplete export dataExport image version differs from control planeAlign image.tag with your Enterprise release (Image Inventory)

Common misconfiguration patterns

Checklist
  1. Port-forward DSN in values — works on your laptop, fails in the cluster. Use an in-cluster service URL.
  2. Different database than Enterprise — export connects but returns no plans found. Use the same database name and DSN as the control plane.
  3. Wrong master password — export fails reading encrypted credentials. Use the exact same secret as the control plane.
  4. License key mismatch — export succeeds but Testkube cannot process the file. Use the same key as your Enterprise install; support can confirm which key applies.
  5. Re-running without a new Helm revision — each helm upgrade creates a new Job. For manual Helm, upgrade again to re-run; the CLI handles this automatically.
  6. Leftover release — from --keep-release or a failed CLI run after install. Run helm uninstall testkube-usage-export -n <namespace>.

When to contact support

If issues persist after checking this guide, gather:

  • CLI --output path (if any)
  • Export pod logs
  • Output of helm list -n <namespace>
  • Enterprise version
  • --timeout value used

Contact Testkube support with the encrypted zip and the context above.

What to do next

  1. Confirm the zip was saved to your --output path.
  2. Send the file to Testkube support or your account team for license usage processing. Use Contact Testkube if you are unsure where to submit it.
  3. Store and transmit the file securely; it is tied to your license key.
  4. If you used --keep-release or manual Helm, clean up: helm uninstall testkube-usage-export -n <namespace>.