# Install air-gapped (/deploy/install/air-gapped)



This page installs a KubeMQ cluster on Kubernetes with no internet access. You download everything on a connected machine, move it in, and license the cluster with an offline license file. Then `kmq license` shows every server active. Time: about 60 minutes.

You work in two sittings: steps 1–4, a wait for your offline license file, then steps 5–7. Need an offline Docker server? [Contact support](mailto:support@kubemq.io).

These commands are for bash on macOS and Linux. On Windows, run them in WSL (Windows Subsystem for Linux), or use the kmq tab of [Try KubeMQ](/deploy/quickstart), which runs natively on Windows.

<Mermaid
  chart="graph LR
  Workstation[&#x22;Connected machine<br/>kmq&#x22;]
  KubeMQ[&#x22;KubeMQ&#x22;]

  subgraph Inside[&#x22;Inside your network&#x22;]
    Admin[&#x22;Administration machine<br/>kmq, Helm, kubectl&#x22;]
    Registry[&#x22;Private registry&#x22;]
    Cluster[&#x22;Kubernetes cluster<br/>operator and 3 servers&#x22;]
  end

  Workstation -- &#x22;kubemq-bundle&#x22; --> Admin
  Admin -- &#x22;push images&#x22; --> Registry
  Registry -- &#x22;pull images&#x22; --> Cluster
  Admin -- &#x22;Helm install&#x22; --> Cluster
  Admin -. &#x22;fingerprint.json&#x22; .-> Workstation
  Workstation -. &#x22;fingerprint.json&#x22; .-> KubeMQ
  KubeMQ -. &#x22;kubemq.license&#x22; .-> Workstation
  Workstation -. &#x22;kubemq.license&#x22; .-> Admin
  Admin -. &#x22;license Secret&#x22; .-> Cluster

  class Workstation,Admin client
  class Registry data
  class Cluster broker
  class KubeMQ external"
/>

## Before you start [#before-you-start]

Do this on both machines.

Create a private folder, readable only by you, and work inside it:

```bash
mkdir -p -m 700 kubemq-private
```

```bash
cd kubemq-private
```

* kmq on the connected machine ([Try KubeMQ](/deploy/quickstart#install-kmq)).
* A private registry the cluster nodes can pull from.
* The other Helm prerequisites of [Install on Kubernetes](/deploy/install/kubernetes#before-you-start).
* An offline license file, requested in step 4 ([Plans compared](/licensing#compare-the-options)).

## Steps [#steps]



<Steps>
  <Step>
    ### Download the bundle [#download-the-bundle]

    On the connected machine:

    ```bash
    kmq deploy artifacts pull --out kubemq-bundle --registry YOUR_MIRROR_REGISTRY --platform linux/amd64 --deadline 30m
    ```

    Replace: `YOUR_MIRROR_REGISTRY` — your registry's address, as the cluster nodes reach it.

    You should see:

    ```text title="Output"
    HARNESS_OUTPUT_PENDING
    ```

    Set `--platform` to the administration machine's: `linux/amd64`, `linux/arm64`, `darwin/amd64` or `darwin/arm64`. The checksummed bundle holds the latest release, the tools and a `mirror-values.yaml` for your registry. Verify it:

    ```bash
    kmq deploy artifacts inspect --manifest kubemq-bundle/artifacts.json
    ```

    You should see:

    ```text title="Output"
    HARNESS_OUTPUT_PENDING
    ```
  </Step>

  <Step>
    ### Move the bundle in and mirror the images [#move-the-bundle-in-and-mirror-the-images]

    Copy `kubemq-bundle` into `kubemq-private` on the administration machine and install kmq:

    ```bash
    sudo install -m 755 kubemq-bundle/bin/kmq /usr/local/bin/
    ```

    If Helm or kubectl is missing:

    ```bash
    sudo install -m 755 kubemq-bundle/bin/helm /usr/local/bin/
    ```

    ```bash
    sudo install -m 755 kubemq-bundle/bin/kubectl /usr/local/bin/
    ```

    ```bash
    kmq deploy artifacts inspect --manifest kubemq-bundle/artifacts.json
    ```

    You should see:

    ```text title="Output"
    HARNESS_OUTPUT_PENDING
    ```

    ```bash
    kmq deploy artifacts push --manifest kubemq-bundle/artifacts.json --registry YOUR_MIRROR_REGISTRY --deadline 30m
    ```

    You should see:

    ```text title="Output"
    HARNESS_OUTPUT_PENDING
    ```
  </Step>

  <Step>
    ### Install the operator [#install-the-operator]

    ```bash
    kubectl --context YOUR_KUBE_CONTEXT create namespace kubemq --dry-run=client -o yaml | kubectl --context YOUR_KUBE_CONTEXT apply -f -
    ```

    Replace: `YOUR_KUBE_CONTEXT` — the target cluster's kubectl context.

    You should see:

    ```text title="Output"
    HARNESS_OUTPUT_PENDING
    ```

    ```bash
    helm upgrade --install kubemq-operator kubemq-bundle/chart/kubemq-next.tgz \
      --kube-context YOUR_KUBE_CONTEXT \
      -n kubemq \
      -f kubemq-bundle/mirror-values.yaml \
      --set operator.enabled=true \
      --set cluster.enabled=false \
      --wait
    ```

    You should see:

    ```text title="Output"
    HARNESS_OUTPUT_PENDING
    ```



    Your mirror must allow anonymous pulls from cluster nodes. If it requires credentials, [contact support](mailto:support@kubemq.io).
  </Step>

  <Step>
    ### Request your offline license file [#request-your-offline-license-file]



    ```bash
    kmq license fingerprint --kube-context YOUR_KUBE_CONTEXT --namespace kubemq --out fingerprint.json
    ```

    You should see:

    ```text title="Output"
    HARNESS_OUTPUT_PENDING
    ```

    Send `fingerprint.json` and your server count through [Plans compared](/licensing#buy). Put the `kubemq.license` you receive in `kubemq-private`.
  </Step>

  <Step>
    ### Apply the offline license file [#apply-the-offline-license-file]

    ```bash
    kubectl --context YOUR_KUBE_CONTEXT -n kubemq create secret generic messaging-license --from-file=licenseFile=kubemq.license --dry-run=client -o yaml | kubectl --context YOUR_KUBE_CONTEXT apply -f -
    ```

    You should see:

    ```text title="Output"
    HARNESS_OUTPUT_PENDING
    ```
  </Step>

  <Step>
    ### Install the cluster [#install-the-cluster]

    ```yaml title="cluster-values.yaml"
    operator:
      enabled: false
    cluster:
      enabled: true
    fullnameOverride: messaging
    replicas: 3
    licenseFileSecretRef:
      name: messaging-license
      key: licenseFile
    image:
      pullPolicy: IfNotPresent
    volume:
      size: 20Gi
      storageClass: YOUR_STORAGE_CLASS
    api:
      expose: ClusterIP
      auth:
        enable: true
        adminUsername: admin
    env:
      STORE_NEXT_ACK_POLICY: "strict"
    ```

    Replace: `YOUR_STORAGE_CLASS` — a storage class from `kubectl --context YOUR_KUBE_CONTEXT get storageclass`.

    As on [Install on Kubernetes](/deploy/install/kubernetes), with the offline license file as the only license source.



    `IfNotPresent` lets a server restart without the registry.

    ```bash
    helm upgrade --install messaging kubemq-bundle/chart/kubemq-next.tgz \
      --kube-context YOUR_KUBE_CONTEXT \
      -n kubemq \
      -f kubemq-bundle/mirror-values.yaml \
      -f cluster-values.yaml
    ```

    You should see:

    ```text title="Output"
    HARNESS_OUTPUT_PENDING
    ```

    ```bash
    kubectl --context YOUR_KUBE_CONTEXT -n kubemq wait --for=condition=Ready kubemqclusters.next.kubemq.io/messaging --timeout=10m
    ```

    You should see:

    ```text title="Output"
    HARNESS_OUTPUT_PENDING
    ```
  </Step>

  <Step>
    ### Check the license [#check-the-license]

    Expect every server `active`, with `EXPIRES` on your file's end date.

    ```bash
    kmq license --kube-context YOUR_KUBE_CONTEXT
    ```

    You should see:

    ```text title="Output"
    HARNESS_OUTPUT_PENDING
    ```

    Other states: [How licensing works](/licensing/how-it-works#check-license-status). Test messages: [Install on Kubernetes](/deploy/install/kubernetes#send-a-test-message).
  </Step>
</Steps>

<Accordions>
  <Accordion title="Pin a version">
    Pinning kmq pins the bundle.



    Open the [Release notes](/release-notes) and copy the release number exactly as shown.

    If kmq is installed, switch it to that release:

    ```bash
    kmq update --version YOUR_VERSION
    ```

    Replace: `YOUR_VERSION` — the release number you copied, with a leading `v`.

    On a machine without kmq, download the installer as [Try KubeMQ](/deploy/quickstart#install-kmq) shows, then run it with that release:

    ```bash
    sh install-kmq.sh --version YOUR_VERSION
    ```

    On Windows, in PowerShell:

    ```powershell
    & .\install-kmq.ps1 -Version YOUR_VERSION
    ```

    That kmq installs its matching server release. A pinned install does not update itself: read the release notes and move to the latest release regularly. `kmq update` with no `--version` returns kmq to the latest release.
  </Accordion>
</Accordions>

### Renew before it expires [#renew-before-it-expires]

The file does not renew itself: the servers stop on the `EXPIRES` date `kmq license` shows. Monitor it as [How licensing works](/licensing/how-it-works#check-license-status) describes. To renew, delete `fingerprint.json`, repeat step 4, replace `kubemq.license`, and repeat steps 5 and 7. Servers restart one at a time, keeping their data.

## If something goes wrong [#if-something-goes-wrong]

* **`ImagePullBackOff`.** An image is missing from your registry or needs credentials (steps 2 and 3).
* **No servers start.** If `kubectl describe` on the `messaging` cluster shows `LicenseInvalid`, check that the Secret's key is `licenseFile` (step 5; [Troubleshooting](/licensing/troubleshooting#invalid-signature)).
* **A server logs `#fingerprint-mismatch`.** Usually the server account cannot read `kube-system` ([Troubleshooting](/licensing/troubleshooting#missing-kube-system-permission)); otherwise the file is for another cluster ([Troubleshooting](/licensing/troubleshooting#fingerprint-mismatch)).
* **The servers stopped on the `EXPIRES` date.** Renew the file ([Troubleshooting](/licensing/troubleshooting#offline-expired)).

## Next steps [#next-steps]

To upgrade, delete `kubemq-bundle`, repeat steps 1 and 2, apply the new custom resource definitions, then repeat steps 3 and 6:

```bash
helm show crds kubemq-bundle/chart/kubemq-next.tgz | kubectl --context YOUR_KUBE_CONTEXT apply --server-side --force-conflicts -f -
```

Replace: `YOUR_KUBE_CONTEXT` — the target cluster's kubectl context.

<Cards>
  <Card title="Production checklist" href="/deploy/production-checklist" description="Harden the cluster before production traffic." />

  <Card title="Upgrade KubeMQ" href="/deploy/upgrade" description="Move a running KubeMQ to a newer release." />

  <Card title="Plans compared" href="/licensing" description="Compare offline license files with the other license kinds." />
</Cards>
