# Install KubeMQ with Docker (/deploy/docker)



<Callout type="warn">
  Docker is recommended for **development and testing only**. For production, deploy on Kubernetes with [Helm and the operator](/deploy/kubernetes-helm).
</Callout>

New here? Start with the [Quickstart](/deploy/quickstart) — it runs KubeMQ, opens the dashboard, and round-trips your first message in about 5 minutes. This page is the in-depth Docker reference: Compose, configuration, connectors, persistence, health checks, and standalone upgrade/rollback/backup.

## Docker Compose [#docker-compose]

For a more structured setup, use Docker Compose. This configuration includes persistent storage and environment variable support.

```yaml title="docker-compose.yml"

services:
  kubemq:
    image: europe-docker.pkg.dev/kubemq/images/kubemq:next
    container_name: kubemq
    hostname: kubemq
    ports:
      - "50000:50000"  # gRPC
      - "9090:9090"    # REST
      - "8080:8080"    # API & Dashboard
    environment:
      - KUBEMQ_TOKEN=${KUBEMQ_TOKEN:-}
    volumes:
      - kubemq-data:/kubemq/store
    restart: unless-stopped

volumes:
  kubemq-data:
```

Save this file and start KubeMQ:

```bash title="Terminal"
docker compose up -d
```

A downloadable version of this file is available at [/docker-compose.yml](/docker-compose.yml).

### Stop and remove [#stop-and-remove]

```bash title="Terminal"
docker compose down
```

To also remove persistent data:

```bash title="Terminal"
docker compose down -v
```

## Configuration [#configuration]

KubeMQ is configured through environment variables. Pass them using the `-e` flag with `docker run` or the `environment` section in Docker Compose.

### Common environment variables [#common-environment-variables]

| Variable                         | Default | Description                                                   |
| -------------------------------- | ------- | ------------------------------------------------------------- |
| `KUBEMQ_TOKEN`                   | —       | License key (required for standalone mode outside Kubernetes) |
| `LOG_LEVEL`                      | `2`     | Log verbosity: 0=Trace 1=Debug 2=Info 3=Warn 4=Error 5=Fatal  |
| `API_PORT`                       | `8080`  | Dashboard and management API port                             |
| `CONNECTORS_GRPC_PORT`           | `50000` | gRPC transport port                                           |
| `CONNECTORS_REST_PORT`           | `9090`  | REST transport port                                           |
| `STORE_CLEAN_STORE`              | `false` | Remove stored data on startup                                 |
| `STORE_MAX_RETENTION`            | `1440`  | Max message retention in minutes (default: 24 hours)          |
| `STORE_MAX_MESSAGES`             | `0`     | Max messages per channel (0 = unlimited)                      |
| `STORE_MAX_QUEUE_SIZE`           | `0`     | Max queue size in bytes (0 = unlimited)                       |
| `QUEUE_MAX_NUMBER_OF_MESSAGES`   | `1024`  | Max messages per receive request                              |
| `QUEUE_MAX_WAIT_TIMEOUT_SECONDS` | `3600`  | Max polling wait timeout in seconds                           |

### Example with configuration [#example-with-configuration]

<RunKubeMQ env="{ LOG_LEVEL: '2', STORE_MAX_RETENTION: '2880' }" />

**Sizing for local eval:** a single container is light — 1-2 vCPU and 2 GB RAM is enough for local development and evaluation traffic on any of the four patterns. Scale up only if you drive sustained high-throughput persistence (Events Store, Queues, or the Kafka connector) with a large `STORE_MAX_RETENTION`. Production/cluster sizing lives in the Helm [production checklist](/deploy/kubernetes-helm#sizing-eval-vs-production).

## Ports [#ports]

KubeMQ exposes three core network ports by default. Wire-protocol connectors are opt-in — they open additional ports only when you enable them.

| Port    | Protocol      | Service          | Description                                                                              |
| ------- | ------------- | ---------------- | ---------------------------------------------------------------------------------------- |
| `50000` | gRPC (HTTP/2) | Transport        | Primary SDK transport. All SDKs connect here by default.                                 |
| `9090`  | HTTP          | REST / WebSocket | REST API and WebSocket connections for subscriptions.                                    |
| `8080`  | HTTP          | Dashboard / API  | Web dashboard, health probes (`/health`, `/ready`), and Prometheus metrics (`/metrics`). |

<Callout type="info">
  The gRPC port (50000) is the primary transport used by all KubeMQ SDKs. The REST port (9090) is useful for testing with cURL or when gRPC is not available. The dashboard port (8080) provides a management UI and health endpoints.
</Callout>

## Enabling wire-protocol connectors [#enabling-wire-protocol-connectors]

The seven wire-protocol connectors (MQTT, AMQP 0-9-1, AMQP 1.0, STOMP, Kafka, AWS, GCP Pub/Sub) are
**disabled by default**. Each opens a new network port and must be explicitly enabled. Enable
them with environment variables:

| Connector             | Env var                         | Ports opened             |
| --------------------- | ------------------------------- | ------------------------ |
| MQTT                  | `CONNECTORSMQTT_ENABLE=true`    | 1883 / 8883 / 8083       |
| AMQP 0-9-1 (RabbitMQ) | `CONNECTORS_AMQP_ENABLE=true`   | 5672 / 5671              |
| AMQP 1.0              | `CONNECTORS_AMQP10_ENABLE=true` | 5672 / 5671 (shared mux) |
| STOMP                 | `CONNECTORS_STOMP_ENABLE=true`  | 61613 / 61614            |
| Kafka                 | `CONNECTORS_KAFKA_ENABLE=true`  | 9092 / 9093              |
| AWS (SQS & SNS)       | `CONNECTORS_AWS_ENABLE=true`    | 4566                     |
| GCP Pub/Sub           | `CONNECTORS_GCP_ENABLE=true`    | 8085                     |

Example — start KubeMQ with MQTT and the AWS connector enabled:

<RunKubeMQ ports="[50000, 9090, 8080, 1883, 4566]" env="{ CONNECTORSMQTT_ENABLE: 'true', CONNECTORS_AWS_ENABLE: 'true' }" />

<Callout type="warn">
  **Env var spelling matters.** The MQTT enable variable is `CONNECTORSMQTT_ENABLE` — no
  underscore between `CONNECTORS` and `MQTT`. Other connectors use an underscore:
  `CONNECTORS_AMQP_ENABLE`, `CONNECTORS_STOMP_ENABLE`, etc. See the
  [connectors reference](/configure/reference/connectors) for all variable names.
</Callout>

## Kafka single-node (Docker) [#kafka-single-node-docker]

KubeMQ can expose a Kafka wire-protocol endpoint for single-node Docker development.
Kafka runs on the **`next` storage engine** — which is **auto-selected with zero
configuration** on a fresh store when the Kafka connector is enabled. You do not set
the engine first; see [Storage Engines](/configure/reference/storage-engines) for
the full selection rules. Enable Kafka with `CONNECTORS_KAFKA_ENABLE=true`.

### User-defined network [#user-defined-network]

Docker's default bridge network has no DNS, so a Kafka client resolving the advertised
container name will not be able to connect there. Create a user-defined network first,
then attach KubeMQ to it with a stable `--hostname`:

```bash title="Terminal"
docker network create mynet

docker run -d \
  --network mynet \
  --name kubemq \
  --hostname kubemq \
  -e KUBEMQ_TOKEN=YOUR_LICENSE_KEY \
  -e CONNECTORS_KAFKA_ENABLE=true \
  europe-docker.pkg.dev/kubemq/images/kubemq:next
```

Other containers attached to `mynet` reach Kafka at `kubemq:9092`.

### Port-mapped host access [#port-mapped-host-access]

To connect from a Kafka client running on the host (outside Docker), advertise
`localhost` and publish the Kafka port. The published host port must equal
`CONNECTORS_KAFKA_PORT` (`9092` by default):

<RunKubeMQ ports="[9092]" env="{ CONNECTORS_KAFKA_ENABLE: 'true', CONNECTORS_KAFKA_ADVERTISED_HOST: 'localhost' }" />

A Kafka client on the host can now connect at `localhost:9092`.

### Persistent volume [#persistent-volume]

Give the container a stable `--name`/`--hostname` so the advertised name and the
store's host-subdir stay stable across restarts, and mount a volume for `/store`:

```bash title="Terminal"
docker run -d \
  --name kubemq \
  --hostname kubemq \
  -e KUBEMQ_TOKEN=YOUR_LICENSE_KEY \
  -e CONNECTORS_KAFKA_ENABLE=true \
  -v kubemq-kafka-data:/kubemq/store \
  europe-docker.pkg.dev/kubemq/images/kubemq:next
```

<Callout type="warn">
  **The user-defined-network and port-mapped-host recipes above are mutually exclusive per
  deployment.** A single advertised endpoint means you pick either user-defined-network
  reachability or port-mapped host reachability — not both. See the
  [Kafka connector](/connectors/kafka) overview and the
  [Kafka settings reference](/configure/reference/connectors#kafka) for the full set
  of options (TLS port, SASL mechanisms, OAUTHBEARER, and more).
</Callout>

## Persistence [#persistence]

By default, KubeMQ stores data inside the container at `/store`. This data is lost when the container is removed. To persist data across container restarts, mount a volume.

### Named volume [#named-volume]

<RunKubeMQ volume="kubemq-data:/kubemq/store" />

### Bind mount [#bind-mount]

Mount a host directory for easier access to the stored data:

<RunKubeMQ volume="$(pwd)/kubemq-store:/kubemq/store" />

Persistence is required for **Events Store** and **Queues** patterns. Events (fire-and-forget) and RPC patterns do not require persistence, but the store is still used for internal metadata.

## Health checks [#health-checks]

KubeMQ exposes health endpoints on the API port:

```bash title="Terminal"
# Liveness check — is the process running?
curl http://localhost:8080/health

# Readiness check — is the broker ready to accept traffic?
curl http://localhost:8080/ready
```

Add a Docker health check to your `docker run` command:

```bash title="Terminal"
docker run -d \
  --name kubemq \
  -p 50000:50000 \
  -p 9090:9090 \
  -p 8080:8080 \
  -e KUBEMQ_TOKEN=YOUR_LICENSE_KEY \
  --health-cmd="curl -f http://localhost:8080/health || exit 1" \
  --health-interval=10s \
  --health-timeout=5s \
  --health-retries=3 \
  europe-docker.pkg.dev/kubemq/images/kubemq:next
```

## Upgrade, rollback & backup [#upgrade-rollback--backup]

KubeMQ ships on the mandatory `:next` tag — there's no version number to bump. "Upgrading" means re-pulling `:next` and recreating the container underneath the same name; "rolling back" means restarting from an image you deliberately kept on disk, not from a pinned digest. `:next` is the only supported channel — see the Helm [image-tag policy](/deploy/kubernetes-helm#production-checklist) for why pinning by digest or semver isn't the model.

### Upgrade [#upgrade]

Re-pull the image and recreate the container:

```bash title="Terminal"
docker pull europe-docker.pkg.dev/kubemq/images/kubemq:next
docker stop kubemq
docker rm kubemq
```

Then start it again with the same command you used originally (same name, same volume) — Docker resolves `:next` to the image you just pulled:

<RunKubeMQ volume="kubemq-data:/kubemq/store" />

### Roll back [#roll-back]

Because `:next` is a moving tag, "rolling back" means restarting from a **local image you retained before upgrading** — not re-pulling an older digest. Do one of these *before* you upgrade:

```bash title="Terminal"
# Option A — tag the currently-running image under a name you control
docker tag europe-docker.pkg.dev/kubemq/images/kubemq:next kubemq-known-good

# Option B — or just record its image ID
docker inspect --format='{{.Image}}' kubemq
```

If the upgrade goes wrong, stop and remove the new container, then start a container from the retained local image (or ID) instead of `:next`:

```bash title="Terminal"
docker stop kubemq
docker rm kubemq
docker run -d \
  --name kubemq \
  --hostname kubemq \
  -p 50000:50000 \
  -p 9090:9090 \
  -p 8080:8080 \
  -e KUBEMQ_TOKEN=YOUR_LICENSE_KEY \
  -v kubemq-data:/kubemq/store \
  kubemq-known-good
```

<Callout type="warn">
  This is **local image retention**, not registry pinning. `:next` stays the only image reference you pull from the registry — the rollback target lives only on this host, which is why you must tag or record it *before* you re-pull.
</Callout>

### Back up the store [#back-up-the-store]

The `kubemq-data` volume — mounted at `/kubemq/store` in the server container — holds everything **Events Store** and **Queues** persist. Back it up with a throwaway container that mounts the same volume (the path inside the throwaway container is arbitrary):

```bash title="Terminal"
docker run --rm -v kubemq-data:/store -v "$(pwd)":/backup alpine \
  tar czf /backup/kubemq-store-backup.tar.gz -C /store .
```

Restore it the same way, in reverse, onto a fresh volume before starting KubeMQ:

```bash title="Terminal"
docker run --rm -v kubemq-data:/store -v "$(pwd)":/backup alpine \
  sh -c "cd /store && tar xzf /backup/kubemq-store-backup.tar.gz"
```

## Related [#related]

<Cards>
  <Card title="Quickstart" href="/deploy/quickstart">
    Run KubeMQ and send your first message in about 5 minutes.
  </Card>

  <Card title="Install with Helm" href="./kubernetes-helm">
    Deploy KubeMQ to Kubernetes for production use.
  </Card>

  <Card title="Configure KubeMQ" href="/configure/docker">
    Env vars, a mounted config.yaml, the CONFIG variable, and docker-compose.
  </Card>

  <Card title="Kafka connector" href="/connectors/kafka">
    Adopt KubeMQ as a drop-in Kafka broker — what works and how to migrate.
  </Card>
</Cards>
