# Configure Connection, TLS, and Observability (/integrations/spring-boot/how-to/configure-connection-tls-observability)



Every KubeMQ Spring Boot setting is bound under the `kubemq.*` prefix from `KubeMQProperties` and resolved at startup by the auto-configuration. This guide walks the connection settings you will tune most often — broker address and client identity, gRPC timeouts and keep-alive, TLS/mTLS, auth tokens, per-environment profiles, and the health, metrics, and tracing surfaces — using the exact property names and defaults the starter ships with.

All snippets assume the starter is on the classpath:

```kotlin title="build.gradle.kts"
dependencies {
    implementation("io.kubemq:kubemq-spring-boot-starter:1.0.0")
}
```

And a broker to connect to — the gRPC API listens on `50000`, the shared HTTP/REST and dashboard endpoints on `9090`:

<RunKubeMQ ports="[50000, 9090]" />

## Prerequisites [#prerequisites]

* `kubemq-spring-boot-starter` on the classpath, in a Spring Boot 3.2.0+ application (see [Getting Started with Spring Boot](/integrations/spring-boot/tutorials/getting-started))
* The broker above running and reachable
* `spring-boot-starter-actuator` and a Micrometer registry (for example `micrometer-registry-prometheus`) added if you use the [Health](#health) or [Metrics & Tracing](#metrics--tracing) sections below

## Connection Basics [#connection-basics]

The four top-level properties establish *which* broker the application talks to and *who* it is. `kubemq.enabled` is the master switch for the entire auto-configuration — set it to `false` to disable KubeMQ wiring without removing the dependency (useful for tests or environments where the broker is absent).

```yaml title="application.yml"
kubemq:
  enabled: true                # master switch; default true
  address: localhost:50000     # broker gRPC address, host:port
  client-id: my-service        # sent with every request; empty = SDK generates a UUID
  auth-token: ${KUBEMQ_AUTH_TOKEN:}  # JWT/OIDC token; never hardcode
```

The defaults from `KubeMQProperties` are `address: localhost:50000`, an empty `client-id` (the SDK generates a UUID per process), and an empty `auth-token`. Externalize the address with a placeholder so the same artifact runs against any broker, exactly as the `connection-auth-token` example does:

```yaml title="connection-auth-token/application.yml"
spring:
  application:
    name: kubemq-example-connection-auth-token
  main:
    web-application-type: none

kubemq:
  # Broker address (override with KUBEMQ_ADDRESS env var)
  address: ${KUBEMQ_ADDRESS:localhost:50000}
  # Unique client identifier for this example
  client-id: spring-connection-auth-token
  # WARNING: Never commit auth tokens to source control. Use environment variables only.
  auth-token: ${KUBEMQ_AUTH_TOKEN:}
```

<Callout type="info">
  Give every service a stable, descriptive `client-id`. It shows up in broker-side dashboards and connection metadata, which makes diagnosing "who is connected" far easier than reading randomly generated UUIDs.
</Callout>

## Connection Tuning [#connection-tuning]

The `kubemq.connection.*` block (bound to `KubeMQProperties.Connection`) controls the gRPC channel itself: the request timeout, the maximum inbound message size, and keep-alive ping behaviour. The defaults are tuned for typical workloads.

```yaml title="application.yml"
kubemq:
  connection:
    timeout: 30s              # default 30s — per-request gRPC deadline
    max-receive-size: 100MB   # default 100MB — max inbound message size
    keep-alive:
      time: 30s               # default 30s — interval between keep-alive pings
      timeout: 10s            # default 10s — wait for a ping ack before failing
```

<TypeTable
  type="{
  &#x22;connection.timeout&#x22;: { type: &#x22;Duration&#x22;, default: &#x22;30s&#x22;, description: &#x22;Per-request gRPC deadline.&#x22; },
  &#x22;connection.max-receive-size&#x22;: { type: &#x22;DataSize&#x22;, default: &#x22;100MB&#x22;, description: &#x22;Maximum inbound message size; raise it for large payloads.&#x22; },
  &#x22;connection.keep-alive.time&#x22;: { type: &#x22;Duration&#x22;, default: &#x22;30s&#x22;, description: &#x22;Interval between keep-alive pings on an idle channel.&#x22; },
  &#x22;connection.keep-alive.timeout&#x22;: { type: &#x22;Duration&#x22;, default: &#x22;10s&#x22;, description: &#x22;How long to wait for a ping ack before considering the channel dead.&#x22; },
}"
/>

Durations accept Spring's ISO-style suffixes (`30s`, `2m`, `500ms`) and `DataSize` accepts `100MB`, `512KB`, and similar. Raise `max-receive-size` if you publish payloads larger than 100 MB; shorten `keep-alive.time` if you sit behind a proxy that aggressively closes idle connections.

## TLS and mTLS [#tls-and-mtls]

TLS is configured under `kubemq.tls.*` (bound to `KubeMQProperties.Tls`) and is disabled by default. There are two postures:

* **One-way (server) TLS** — the client verifies the broker's certificate against a CA bundle. Set `enabled: true` and `ca-cert-file` (plus `cert-file` if your broker requires the client to present one).
* **Mutual TLS (mTLS)** — both sides authenticate. Provide `cert-file` *and* `key-file` for the client identity, and `ca-cert-file` to verify the broker.

<Tabs items="[&#x22;One-way TLS&#x22;, &#x22;Mutual TLS&#x22;]">
  <Tab value="One-way TLS">
    ```yaml title="connection-tls/application.yml"
    kubemq:
      address: ${KUBEMQ_ADDRESS:localhost:50000}
      client-id: spring-connection-tls
      tls:
        enabled: true
        cert-file: ${KUBEMQ_TLS_CERT:/path/to/client.crt}
        ca-cert-file: ${KUBEMQ_TLS_CA_CERT:/path/to/ca.crt}
        # Ensure the broker certificate SAN matches the hostname in kubemq.address.
    ```
  </Tab>

  <Tab value="Mutual TLS">
    ```yaml title="connection-mtls/application.yml"
    kubemq:
      address: ${KUBEMQ_ADDRESS:localhost:50000}
      client-id: spring-connection-mtls
      tls:
        enabled: true
        cert-file: ${KUBEMQ_TLS_CERT:/path/to/client.crt}
        key-file: ${KUBEMQ_TLS_KEY:/path/to/client.key}
        ca-cert-file: ${KUBEMQ_TLS_CA_CERT:/path/to/ca.crt}
        # Ensure the broker certificate SAN matches the hostname in kubemq.address.
    ```
  </Tab>
</Tabs>

<TypeTable
  type="{
  &#x22;tls.enabled&#x22;: { type: &#x22;boolean&#x22;, default: &#x22;false&#x22;, description: &#x22;Turn TLS on for the gRPC channel.&#x22; },
  &#x22;tls.cert-file&#x22;: { type: &#x22;String&#x22;, default: '&#x22;&#x22;', description: &#x22;Path to the client certificate (required for mTLS).&#x22; },
  &#x22;tls.key-file&#x22;: { type: &#x22;String&#x22;, default: '&#x22;&#x22;', description: &#x22;Path to the client private key (required for mTLS).&#x22; },
  &#x22;tls.ca-cert-file&#x22;: { type: &#x22;String&#x22;, default: '&#x22;&#x22;', description: &#x22;Path to the CA bundle used to verify the broker certificate.&#x22; },
}"
/>

<Callout type="warn">
  The broker certificate's SAN must match the hostname in `kubemq.address`, and hostname verification should never be disabled in production. Keep certificate and key paths out of source control — point them at mounted secrets or environment-resolved paths.
</Callout>

## Auth Tokens [#auth-tokens]

`kubemq.auth-token` carries a JWT or OIDC token that the broker validates on connect. It is sent with every request alongside the `client-id`. Because it is a credential, resolve it from the environment and never write the literal value into a committed file:

```yaml title="application.yml"
kubemq:
  address: ${KUBEMQ_ADDRESS:localhost:50000}
  client-id: my-service
  auth-token: ${KUBEMQ_AUTH_TOKEN:}
```

```bash title="Provide the token at runtime"
export KUBEMQ_AUTH_TOKEN="$(cat /var/run/secrets/kubemq/token)"
java -jar my-service.jar
```

An empty `auth-token` (the default) means the application connects without presenting a token — appropriate only when the broker does not enforce authentication.

## Profile-Based Configuration [#profile-based-configuration]

Use Spring profiles to keep per-environment settings in `application-{profile}.yml`. The `autoconfigure-profiles` example ships a base file plus `dev`, `staging`, and `prod` overlays. The base file selects the active profile and holds shared defaults:

```yaml title="application.yml"
spring:
  profiles:
    active: ${SPRING_PROFILES_ACTIVE:dev}

kubemq:
  address: ${KUBEMQ_ADDRESS:localhost:50000}
  client-id: spring-autoconfigure-profiles
```

Each overlay layers only what changes for that environment:

<Tabs items="[&#x22;dev&#x22;, &#x22;staging&#x22;, &#x22;prod&#x22;]">
  <Tab value="dev">
    ```yaml title="application-dev.yml"
    kubemq:
      address: ${KUBEMQ_ADDRESS:localhost:50000}
      client-id: spring-profiles-dev

    logging:
      level:
        io.kubemq: DEBUG
    ```
  </Tab>

  <Tab value="staging">
    ```yaml title="application-staging.yml"
    kubemq:
      address: staging-broker:50000
      client-id: spring-profiles-staging
    ```
  </Tab>

  <Tab value="prod">
    ```yaml title="application-prod.yml"
    kubemq:
      address: ${KUBEMQ_ADDRESS:prod-broker:50000}
      client-id: spring-profiles-prod
      tls:
        # Set paths via env in production — never commit key material or PEM files.
        enabled: ${KUBEMQ_TLS_ENABLED:true}
        ca-cert-file: ${KUBEMQ_TLS_CA_CERT:}
        cert-file: ${KUBEMQ_TLS_CERT:}
        key-file: ${KUBEMQ_TLS_KEY:}
    ```
  </Tab>
</Tabs>

Activate a profile with `--spring.profiles.active=prod` or the `SPRING_PROFILES_ACTIVE` environment variable.

## Health [#health]

The starter contributes two Actuator surfaces, both backed by pings to all three KubeMQ SDK clients (`PubSubClient`, `QueuesClient`, `CQClient`). They are configured under `kubemq.health.*` (bound to `KubeMQProperties.Health`).

```yaml title="application.yml"
kubemq:
  health:
    enabled: true             # default true
    timeout: 5s               # default 5s — per-ping deadline
    cache-duration: 15s       # default 15s — reuse the last result within this window

management:
  endpoints:
    web:
      exposure:
        include: health,info
  endpoint:
    health:
      show-details: when-authorized
```

`KubeMQHealthIndicator` rolls up into the standard Spring Boot `/actuator/health` endpoint: `UP` with `host` and `version` details when every client responds to a ping, `DOWN` with the failing exception type otherwise. Results are cached for `cache-duration` so frequent probes do not flood the broker, and each ping is bounded by `timeout`.

The dedicated `KubeMQEndpoint` exposes `/actuator/kubemq`, which probes the three clients individually and reports a per-client breakdown:

```json title="GET /actuator/kubemq"
{
  "status": "connected",
  "clients": {
    "pubsub": { "status": "connected", "host": "kubemq-host", "version": "2.x.x", "errorType": null },
    "queues": { "status": "connected", "host": "kubemq-host", "version": "2.x.x", "errorType": null },
    "cq":     { "status": "connected", "host": "kubemq-host", "version": "2.x.x", "errorType": null }
  }
}
```

The top-level `status` is `connected` only when all three clients are connected; if any client fails its ping it is marked `disconnected` (with the exception type in `errorType`) and the aggregate becomes `degraded`. Expose the endpoint by adding `kubemq` to the management exposure list:

```yaml title="application.yml"
management:
  endpoints:
    web:
      exposure:
        include: health,info,kubemq
```

<Callout type="warn">
  `show-details: always` and exposing Actuator endpoints without authentication are for local development only. In production use `when-authorized` with Spring Security, or bind the management port to loopback.
</Callout>

## Metrics & Tracing [#metrics--tracing]

The starter integrates with Micrometer in two complementary ways, controlled by separate switches.

`kubemq.metrics.*` (bound to `KubeMQProperties.Metrics`) governs the `kubemq.connection.state` gauge, registered by `KubeMQMetricsAutoConfiguration` when `kubemq.metrics.enabled` is `true` (the default). The gauge probes all three clients and caches the result for `scrape-interval` so scraping does not overload the broker — it reads `1.0` when connected and `0.0` when not.

`kubemq.template.observation-enabled` (bound to `KubeMQProperties.Template`, default `true`) drives Micrometer **Observation** instrumentation on the `KubeMQTemplate` send path and the listener receive path, producing timers, counters, and — when a tracer is present — distributed traces.

```yaml title="observability-micrometer/application.yml"
management:
  endpoints:
    web:
      exposure:
        include: health,prometheus,metrics
  metrics:
    export:
      prometheus:
        enabled: true

kubemq:
  address: ${KUBEMQ_ADDRESS:localhost:50000}
  client-id: spring-observability-micrometer
  metrics:
    enabled: true             # default true — registers kubemq.connection.state gauge
    scrape-interval: 30s      # default 30s — gauge probe caching window
  template:
    observation-enabled: true # default true — send/receive Observation instrumentation
```

Add the Actuator and a registry to the project to scrape these meters:

```kotlin title="build.gradle.kts"
dependencies {
    implementation("io.kubemq:kubemq-spring-boot-starter:1.0.0")
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-actuator")
    implementation("io.micrometer:micrometer-registry-prometheus")
}
```

<TypeTable
  type="{
  &#x22;metrics.enabled&#x22;: { type: &#x22;boolean&#x22;, default: &#x22;true&#x22;, description: &#x22;Register the kubemq.connection.state gauge.&#x22; },
  &#x22;metrics.scrape-interval&#x22;: { type: &#x22;Duration&#x22;, default: &#x22;30s&#x22;, description: &#x22;Caching window for the connection-state probe.&#x22; },
  &#x22;template.observation-enabled&#x22;: { type: &#x22;boolean&#x22;, default: &#x22;true&#x22;, description: &#x22;Emit Micrometer Observations for send and receive operations.&#x22; },
}"
/>

## Kotlin DSL Alternative [#kotlin-dsl-alternative]

The Kotlin starter (`kubemq-spring-boot-starter-kotlin`) adds a type-safe configuration DSL. Expose a `KubeMQConfigurerDsl` bean built with the `kubemq { }` builder to set the address, identity, TLS, and connection tuning programmatically instead of in YAML:

```kotlin title="KubeMQConfig.kt"
import io.kubemq.spring.boot.kotlin.KubeMQConfigurerDsl
import io.kubemq.spring.boot.kotlin.kubemq
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.time.Duration

@Configuration
class KubeMQConfig {
    @Bean
    fun kubemqConfigurer(): KubeMQConfigurerDsl = kubemq {
        address = "broker.example.com:50000"
        clientId = "my-service"
        tls {
            enabled = true
            certFile = "/certs/client.pem"
            keyFile = "/certs/client-key.pem"
            caCertFile = "/certs/ca.pem"
        }
        connection {
            timeout = Duration.ofSeconds(15)
        }
    }
}
```

The `tls { }` and `connection { }` blocks map one-to-one onto the same `KubeMQProperties.Tls` and `KubeMQProperties.Connection` fields shown above. For Kotlin services that drive suspend-function listeners, `kubemq.kotlin.dispatcher` selects the coroutine dispatcher (`default`, `io`, or `unconfined`; default `default`):

```yaml title="application.yml"
kubemq:
  kotlin:
    dispatcher: io   # default | io | unconfined
```

## Graceful Shutdown & Reconnection [#graceful-shutdown--reconnection]

`kubemq.listener.shutdown-timeout` (default `30s`, on `KubeMQProperties.Listener`) bounds how long the listener infrastructure waits for in-flight work to finish during shutdown. Combined with Spring's lifecycle hooks (`SmartLifecycle`, `@PreDestroy`), it lets a service drain cleanly on `SIGTERM`:

```yaml title="application.yml"
kubemq:
  listener:
    shutdown-timeout: 30s   # default 30s — wait for in-flight work before stopping
```

Reconnection is handled automatically by the underlying SDK clients. To observe or react to connection state changes, register a `ConnectionStateListener` on an injected client:

```java title="ReconnectionRunner.java"
@PostConstruct
public void registerConnectionStateListener() {
    pubSubClient.addConnectionStateListener(new ConnectionStateListener() {
        @Override public void onConnected()        { log.info("Connection established"); }
        @Override public void onDisconnected()     { log.warn("Connection lost — reconnecting automatically"); }
        @Override public void onReconnecting(int attempt) { log.info("Reconnection attempt #{}", attempt); }
        @Override public void onReconnected()      { log.info("Reconnection successful — subscriptions recovered"); }
        @Override public void onClosed()           { log.info("Connection closed"); }
    });
}
```

When a listener throws, provide a `KubeMQErrorHandler` bean to centralize error handling instead of relying on the default logging:

```java title="ReconnectionErrorConfig.java"
@Configuration
public class ReconnectionErrorConfig {
    @Bean
    public KubeMQErrorHandler kubemqErrorHandler() {
        return new KubeMQErrorHandler() {
            @Override
            public void handleError(Throwable t) {
                log.error("KubeMQ listener error [{}]: {}", t.getClass().getSimpleName(), t.getMessage());
            }
        };
    }
}
```

## Full Property Reference [#full-property-reference]

This guide covers the connection, security, and observability properties you tune most often. For the complete list of `kubemq.*` properties — including the per-pattern listener settings (`kubemq.listener.queues.*`, `commands`, `queries`) — see the [Configuration Reference](/integrations/spring-boot/reference/configuration); for the `KubeMQTemplate` API and annotations, see the [API Reference](/integrations/spring-boot/reference/api).

## Related [#related]

* [Getting Started](/integrations/spring-boot/tutorials/getting-started) — add the starter and send your first message
* [Concepts](/integrations/spring-boot/concepts) — how auto-configuration wires the SDK clients
* [Configuration Reference](/integrations/spring-boot/reference/configuration) — every configuration property
* [API Reference](/integrations/spring-boot/reference/api) — the template API and listener annotations
