KubeMQ
Licensing

Air-Gapped Installations

License a KubeMQ server with no network access — request a signed file for your fingerprints, pass it with KUBEMQ_LICENSE_FILE or _DATA, and renew it.

An air-gapped server is licensed with a file: a token signed by KubeMQ that the server verifies locally with the public key embedded in its binary. The server never opens an outbound connection — no activation, no refresh, no usage report.

The same file is also the answer for networks that are online but cannot allow egress to the licensing endpoint (a pre-activated file — see Use a license key).

Request a file

A license file is bound to a list of installation fingerprints — the installations allowed to run it. Collect one fingerprint per installation and send them with your request.

Read each installation's fingerprint

The fingerprint is stable per installation: on Kubernetes it is the UID of the kube-system namespace (one per cluster); elsewhere it is an id generated on first boot and persisted at <store>/license/instance.id.

The easiest way to read it is from a running server:

Terminal
curl -s http://localhost:8080/api/v1/license | jq -r '.fingerprint, .fingerprint_source'

If you do not have a licensed server yet: on Kubernetes the fingerprint is simply the cluster's kube-system namespace UID, which you can read directly —

Terminal
kubectl get namespace kube-system -o jsonpath='{.metadata.uid}'

— provided the server's ServiceAccount has the permission described in Kubernetes, so that the running server will derive the same value (fingerprint_source: "kube-system", not persisted). Elsewhere the fingerprint is generated on the server's first boot and persisted at <store>/license/instance.id; the reliable way to obtain it is to run the server once with an online key (a trial key works) on the same store volume you will keep, and read GET /api/v1/license.

Send the list to KubeMQ

Send your fingerprints, the number of installations, and the plan to your KubeMQ contact. Ask for an unbound file (empty fingerprint list) only if your installations are genuinely ephemeral; an unbound file is accepted by any installation.

Receive the armored file

You receive a text file that looks like this:

kubemq.license
-----BEGIN KUBEMQ LICENSE-----
eyJhbGciOiJFUzI1NiIsImtpZCI6ImxpY2Vuc2Utc2lnbmluZy12MiJ9.<PAYLOAD>.<SIGNATURE>
-----END KUBEMQ LICENSE-----

The body is one signed token. Its header names the signing key id (kid) — the server binary trusts license-signing-v1 and license-signing-v2; the payload and signature above are placeholders, not a real license.

Store it like any other secret. It carries your license id, plan, commercial expiry, installation cap and fingerprint list, all signed.

Pass the file to the server

Two variables accept a file. KUBEMQ_LICENSE_FILE takes a path; KUBEMQ_LICENSE_DATA takes the contents (the armored text, or the bare token inside it).

Mount the file read-only and point KUBEMQ_LICENSE_FILE at it:

Terminal
docker run -d \
  --name kubemq \
  --hostname kubemq \
  -p 50000:50000 \
  -p 9090:9090 \
  -p 8080:8080 \
  -e KUBEMQ_LICENSE_FILE=/kubemq/license/kubemq.license \
  -v "$(pwd)/kubemq.license:/kubemq/license/kubemq.license:ro" \
  -v "$(pwd)/kubemq-store:/kubemq/store" \
  europe-docker.pkg.dev/kubemq/images/kubemq-next:latest
docker-compose.yml
services:
  kubemq:
    image: europe-docker.pkg.dev/kubemq/images/kubemq-next:latest
    container_name: kubemq
    hostname: kubemq
    ports:
      - "50000:50000"
      - "9090:9090"
      - "8080:8080"
    environment:
      - KUBEMQ_LICENSE_FILE=/kubemq/license/kubemq.license
    volumes:
      - ./kubemq.license:/kubemq/license/kubemq.license:ro
      - kubemq-data:/kubemq/store
    restart: unless-stopped

volumes:
  kubemq-data:

Store the file in a Secret and reference it from the KubemqCluster; the operator injects its contents as KUBEMQ_LICENSE_DATA:

Terminal
kubectl create secret generic kubemq-license -n kubemq \
  --from-file=license=./kubemq.license
kubemq-cluster.yaml
apiVersion: next.kubemq.io/v1
kind: KubemqCluster
metadata:
  name: kubemq-next
  namespace: kubemq
spec:
  replicas: 3
  licenseFileSecretRef:
    name: kubemq-license
    key: license

With Helm: --set licenseFileSecretRef.name=kubemq-license --set licenseFileSecretRef.key=license.

Inject the Secret value directly as KUBEMQ_LICENSE_DATA:

statefulset-excerpt.yaml
env:
  - name: KUBEMQ_LICENSE_DATA
    valueFrom:
      secretKeyRef:
        name: kubemq-license
        key: license

Or mount the Secret as a file and set KUBEMQ_LICENSE_FILE to the mount path. Either way the pods still need the kube-system read permission described in Kubernetes, because the fingerprint in the file is the cluster's namespace UID.

Precedence

SetResult
KUBEMQ_LICENSE_FILE onlyThe file is used
KUBEMQ_LICENSE_DATA onlyThe data is used
Both FILE and DATAFILE wins; a warning is logged
A file and KUBEMQ_LICENSE_KEYThe file wins; a warning is logged (#both-key-and-file). The key is ignored entirely — no activation, no refresh
NoneThe server refuses to start (#no-license-input)

What the server does — and does not do — with a file

  • Verifies locally, at boot. Signature, signing key id, expiry (with a 5-minute leeway), and that its own fingerprint is in the file's list (or the list is empty). Every minute after that it re-checks only the expiry and its own clock against the persisted high-water mark — the signature and fingerprint are not re-verified while the server runs, so a file swapped or edited on disk takes effect at the next boot.
  • Makes no network calls. There is no activation, no hourly refresh and no usage report. GET /api/v1/license shows mode: "offline", usage.enabled: false, and last_refresh.outcome: "never".
  • Has no grace period. An offline file's exp is a hard date: the server refuses to start after it, and a running server drains and exits with code 3 when it passes (#offline-expired). Renew before that.
  • Keeps no lease cache. The file is the source of truth. The server still writes two things under <store>/license/: the persisted instance id used as a fingerprint outside Kubernetes, and the clock high-water mark (clock.hwm). A clock set back more than 10 minutes behind that mark is a refusal at boot and a stop on a running server (#clock-rollback) — an offline server has no service to confirm the time with.

Renewal

A file is valid for at most one year from issuance and never beyond the commercial expiry of the license. Renewal is a reissue: request a new file before the current one expires, replace it (update the Secret, or swap the mounted file) and restart the server — the file is read at boot.

Check lease_expires_at in GET /api/v1/license (or kmq license) to see the current file's expiry, and plan the reissue ahead of it.

What revocation means for a file

A file that has been issued cannot be revoked. There is no channel through which a revocation could reach an air-gapped server, and the server does not look for one. This is a deliberate property of the design, not a gap: the file's expiry (at most one year), its fingerprint list and your contract are what bound its use.

Consequently, if a file leaks, the remedy is contractual and the exposure ends at the file's expiry. Keep files as tightly controlled as any other credential, and prefer bound files (with a fingerprint list) over unbound ones.

Was this page helpful?

On this page