> ## Documentation Index
> Fetch the complete documentation index at: https://docs.conduktor.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrate Conduktor Gateway networking to listeners

> Move Conduktor Gateway from the deprecated global networking variables to per-listener configuration. Variable mapping, a worked example, and behavior changes to plan for.

## Overview

From Gateway 3.20.0, networking is configured per listener with `GATEWAY_LISTENER_<NAME>_*` variables. The global `GATEWAY_PORT_*`, `GATEWAY_ADVERTISED_HOST`, `GATEWAY_ROUTING_MECHANISM` and `GATEWAY_SECURITY_PROTOCOL` variables — the **legacy network configuration** — are deprecated and will be removed in Gateway 3.23.

This guide shows how to switch an existing deployment to listener configuration. For how the legacy mode behaves and the full deprecated variable reference, see [Legacy network configuration](/guide/conduktor-in-production/deploy-artifacts/deploy-gateway/legacy-network-configuration).

Listener configuration removes the inference behavior of legacy network configuration — you set every relevant value explicitly.

## Map legacy variables to listener variables

The legacy network variables internally create a single listener named `DEFAULT`. Use this mapping as a reference:

| Legacy variable                                                   | Listener equivalent                                                                                    |
| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `GATEWAY_BIND_HOST`                                               | `GATEWAY_LISTENER_<NAME>_BIND_ADDRESS`                                                                 |
| `GATEWAY_ADVERTISED_HOST`                                         | `GATEWAY_LISTENER_<NAME>_ADVERTISED_HOST`                                                              |
| `GATEWAY_PORT_START` and `GATEWAY_PORT_COUNT`                     | `GATEWAY_LISTENER_<NAME>_PORTS`                                                                        |
| `GATEWAY_ADVERTISED_SNI_PORT`                                     | `GATEWAY_LISTENER_<NAME>_PORTS` (use `advertised:local` format)                                        |
| `GATEWAY_ROUTING_MECHANISM`                                       | `GATEWAY_LISTENER_<NAME>_ROUTING` (`host` is now called `sni`)                                         |
| `GATEWAY_SECURITY_PROTOCOL`                                       | `GATEWAY_LISTENER_<NAME>_SECURITY_PROTOCOL`                                                            |
| `GATEWAY_ADVERTISED_HOST_PREFIX` and `GATEWAY_SNI_HOST_SEPARATOR` | `GATEWAY_LISTENER_<NAME>_ADVERTISED_HOST_PATTERN` and `GATEWAY_LISTENER_<NAME>_BOOTSTRAP_HOST_PATTERN` |
| `GATEWAY_SSL_CLIENT_AUTH`                                         | `GATEWAY_LISTENER_<NAME>_SSL_CLIENT_AUTH`                                                              |

These globals stay global rather than moving to a per-listener variable, and keep their names: `GATEWAY_SECURITY_MODE`, `GATEWAY_ACL_ENABLED`, `GATEWAY_SSL_KEY_STORE_*`, `GATEWAY_SSL_TRUST_STORE_*`, and all `KAFKA_*` connection variables. `GATEWAY_SECURITY_MODE` and `GATEWAY_ACL_ENABLED` are no longer inferred, though — see [Behavior differences to plan for](#behavior-differences-to-plan-for).

<Note>
  If your deployment still uses a `DELEGATED_SASL_PLAINTEXT` or `DELEGATED_SASL_SSL` security protocol, migrate it first: the listener `SECURITY_PROTOCOL` only accepts `PLAINTEXT`, `SSL`, `SASL_PLAINTEXT` or `SASL_SSL`. Set `GATEWAY_SECURITY_MODE=KAFKA_MANAGED` and use the plain protocol (`SASL_PLAINTEXT` or `SASL_SSL`) on the listener. See [Migrate security mode](/guide/tutorials/migrate-gateway-security).
</Note>

## Behavior differences to plan for

Three behaviors change when you migrate:

1. **Security mode and ACL flag have to be set explicitly.** Listener configuration does not infer `GATEWAY_SECURITY_MODE` or `GATEWAY_ACL_ENABLED` from the backing Kafka cluster. Set both:
   * `GATEWAY_SECURITY_MODE` to `GATEWAY_MANAGED` or `KAFKA_MANAGED`.
   * `GATEWAY_ACL_ENABLED` to `true` or `false` (typically `true` for `GATEWAY_MANAGED` and `false` for `KAFKA_MANAGED`).
2. **SNI host patterns have changed.** The default advertised host pattern is now `broker-{{physicalCluster}}-{{nodeId}}.{{advertisedHost}}`. This differs from the previous default built from `GATEWAY_ADVERTISED_HOST_PREFIX` (default `broker`) and `GATEWAY_SNI_HOST_SEPARATOR` (default `-`). If your DNS records and TLS certificates match the old pattern, either update them or set the pattern explicitly to reproduce it:

   ```bash theme={null}
   GATEWAY_LISTENER_<NAME>_ADVERTISED_HOST_PATTERN=broker{{physicalCluster}}{{nodeId}}-{{advertisedHost}}
   GATEWAY_LISTENER_<NAME>_BOOTSTRAP_HOST_PATTERN=broker{{physicalCluster}}-{{advertisedHost}}
   ```
3. **mTLS configuration is per listener.** Set `GATEWAY_LISTENER_<NAME>_SSL_CLIENT_AUTH` for each listener (for example, `GATEWAY_LISTENER_EXTERNAL_SSL_CLIENT_AUTH=REQUIRE`). The global `GATEWAY_SSL_CLIENT_AUTH` is ignored in listener configuration.

## Worked example

The migration depends on your routing mechanism. Pick the tab that matches your `GATEWAY_ROUTING_MECHANISM`.

These examples name the listener `DEFAULT` to mirror the listener that legacy mode creates internally, which keeps the mapping one-to-one. The name is a label you choose — use any name that fits your setup (for example, `INTERNAL` or `EXTERNAL`) and apply it consistently across that listener's `GATEWAY_LISTENER_<NAME>_*` variables.

<Tabs>
  <Tab title="Port routing">
    This legacy network configuration:

    ```bash theme={null}
    GATEWAY_BIND_HOST=0.0.0.0
    GATEWAY_ADVERTISED_HOST=gateway.example.com
    GATEWAY_PORT_START=9092
    GATEWAY_PORT_COUNT=6
    GATEWAY_ROUTING_MECHANISM=port
    GATEWAY_SECURITY_PROTOCOL=SASL_PLAINTEXT
    GATEWAY_MIN_BROKERID=1
    ```

    Becomes:

    ```bash theme={null}
    GATEWAY_SECURITY_MODE=GATEWAY_MANAGED
    GATEWAY_ACL_ENABLED=true

    GATEWAY_LISTENER_DEFAULT_BIND_ADDRESS=0.0.0.0
    GATEWAY_LISTENER_DEFAULT_ADVERTISED_HOST=gateway.example.com
    GATEWAY_LISTENER_DEFAULT_PORTS=9092-9097
    GATEWAY_LISTENER_DEFAULT_ROUTING=port
    GATEWAY_LISTENER_DEFAULT_SECURITY_PROTOCOL=SASL_PLAINTEXT
    GATEWAY_MIN_BROKERID=1
    ```

    What changed:

    * `GATEWAY_PORT_START=9092` with `GATEWAY_PORT_COUNT=6` maps to the port range `9092-9097`.
    * `GATEWAY_MIN_BROKERID` is unchanged — it still maps ports to broker IDs in order under listener configuration.
  </Tab>

  <Tab title="SNI routing">
    This legacy network configuration:

    ```bash theme={null}
    GATEWAY_BIND_HOST=0.0.0.0
    GATEWAY_ADVERTISED_HOST=gateway.example.com
    GATEWAY_PORT_START=6969
    GATEWAY_ADVERTISED_SNI_PORT=443
    GATEWAY_ROUTING_MECHANISM=host
    GATEWAY_SECURITY_PROTOCOL=SASL_SSL
    GATEWAY_ADVERTISED_HOST_PREFIX=gateway
    GATEWAY_SNI_HOST_SEPARATOR=-
    ```

    Becomes:

    ```bash theme={null}
    GATEWAY_SECURITY_MODE=GATEWAY_MANAGED
    GATEWAY_ACL_ENABLED=true

    GATEWAY_LISTENER_DEFAULT_BIND_ADDRESS=0.0.0.0
    GATEWAY_LISTENER_DEFAULT_ADVERTISED_HOST=gateway.example.com
    GATEWAY_LISTENER_DEFAULT_PORTS=443:6969
    GATEWAY_LISTENER_DEFAULT_ROUTING=sni
    GATEWAY_LISTENER_DEFAULT_SECURITY_PROTOCOL=SASL_SSL
    GATEWAY_LISTENER_DEFAULT_ADVERTISED_HOST_PATTERN=gateway{{physicalCluster}}{{nodeId}}-{{advertisedHost}}
    GATEWAY_LISTENER_DEFAULT_BOOTSTRAP_HOST_PATTERN=gateway{{physicalCluster}}-{{advertisedHost}}
    ```

    What changed:

    * SNI routing uses a single port, so `GATEWAY_PORT_START=6969` and `GATEWAY_ADVERTISED_SNI_PORT=443` map to one `advertised:local` mapping, `443:6969`. Gateway listens locally on port `6969` but advertises port `443` to clients.
    * `GATEWAY_ADVERTISED_HOST_PREFIX` and `GATEWAY_SNI_HOST_SEPARATOR` are replaced by explicit host patterns using template variables.
  </Tab>
</Tabs>

## Verify the migration

Restart Gateway and check the startup logs. Confirm that:

* The legacy deprecation warning (`Legacy environment variables detected (GATEWAY_PORT_START, etc.)`) no longer appears. If it does, a legacy variable is still set — Gateway refuses to start when legacy variables and an explicit listener configuration are both present, so unset every legacy variable it names.
* The `Computed configuration` block lists each listener you defined, with the ports, advertised host, routing and security protocol you intended.

For the SNI worked example above, the block lists the `DEFAULT` listener (this output is from Gateway 3.20.0 and may change in later versions):

```
[main] [INFO] [Bootstrap] - Computed configuration:
---
listeners:
  DEFAULT:
    security:
      protocol: "SASL_SSL"
    routing: "sni"
    ports:
    - "443:6969"
    advertisedHost: "gateway.example.com"
...
```

Run a Kafka client against the new listener (for example, with [kcat](https://github.com/edenhill/kcat) <Icon icon="up-right-from-square" />'s metadata list mode `-L`) to confirm the metadata response advertises the expected hostnames and ports.

## Kubernetes (Helm chart)

The [Conduktor Gateway Helm chart](https://github.com/conduktor/conduktor-public-charts/tree/main/charts/gateway) manages listener environment variables for you. From chart version 3.21.0, multi-listener mode is the default — the chart translates `gateway.listeners.*` values into `GATEWAY_LISTENER_<NAME>_*` env vars automatically.

The chart creates two listeners — `INTERNAL` (cluster traffic) and `EXTERNAL` (client traffic, enabled via `service.external.enable: true`) — each with its own ports, security protocol and routing mode.

### Upgrade from a chart version before 3.21.0

Previous chart versions used `gateway.portRange` (which generated `GATEWAY_PORT_START` and `GATEWAY_PORT_COUNT`). On upgrade you have two options:

**Option A — Preserve legacy behavior temporarily**

Add `gateway.portRange.enable: true` to keep the old single-listener mode while you plan the migration:

```yaml theme={null}
gateway:
  portRange:
    enable: true
    start: 9092
    count: 7
```

<Warning>
  This is deprecated and will be removed in a future chart release. Use it only as a stopgap while you migrate to `gateway.listeners`.
</Warning>

**Option B — Migrate to multi-listeners (recommended)**

Replace `gateway.portRange` with `gateway.listeners`. The migration depends on your routing mode.

<Tabs>
  <Tab title="Port routing">
    ```yaml theme={null}
    # Before (chart < 3.21.0)
    gateway:
      portRange:
        start: 9092
        count: 6
      env:
        GATEWAY_SECURITY_PROTOCOL: "SASL_PLAINTEXT"
        GATEWAY_ROUTING_MECHANISM: "port"
        GATEWAY_ADVERTISED_HOST: "gateway.example.com"
    ```

    Becomes:

    ```yaml theme={null}
    # After (chart >= 3.21.0)
    gateway:
      securityMode: "GATEWAY_MANAGED"
      listeners:
        internal:
          securityProtocol: SASL_PLAINTEXT
          routing: port
          ports:
            - "9092-9097"
    ```

    Port routing does not require an external listener — all traffic goes through the internal service.
  </Tab>

  <Tab title="SNI routing">
    ```yaml theme={null}
    # Before (chart < 3.21.0)
    gateway:
      portRange:
        start: 6969
        count: 1
      env:
        GATEWAY_SECURITY_PROTOCOL: "SASL_SSL"
        GATEWAY_ROUTING_MECHANISM: "host"
        GATEWAY_ADVERTISED_HOST: "gateway.example.com"
        GATEWAY_ADVERTISED_SNI_PORT: "443"
    ```

    Becomes:

    ```yaml theme={null}
    # After (chart >= 3.21.0)
    gateway:
      securityMode: "GATEWAY_MANAGED"
      kafka:
        brokerIds:
          - "0-2"                                # match your Kafka broker IDs
      listeners:
        internal:
          securityProtocol: SASL_SSL
          routing: sni
          ports:
            - "19092"                             # internal port (must not overlap external)
        external:
          securityProtocol: SASL_SSL
          routing: sni
          ports:
            - "6969:443"                          # local:advertised
          advertisedHost: "gateway.example.com"
          advertisedHostPattern: "broker-{{nodeId}}.gateway.example.com"

    service:
      external:
        enable: true
        type: LoadBalancer
    ```
  </Tab>
</Tabs>

### Helm values to listener variable mapping

The chart translates these `values.yaml` paths into the listener env vars from the [mapping table above](#map-legacy-variables-to-listener-variables):

| Helm value                                       | Generated env var                                 |
| ------------------------------------------------ | ------------------------------------------------- |
| `gateway.securityMode`                           | `GATEWAY_SECURITY_MODE`                           |
| `gateway.aclEnabled`                             | `GATEWAY_ACL_ENABLED`                             |
| `gateway.listeners.<name>.securityProtocol`      | `GATEWAY_LISTENER_<NAME>_SECURITY_PROTOCOL`       |
| `gateway.listeners.<name>.routing`               | `GATEWAY_LISTENER_<NAME>_ROUTING`                 |
| `gateway.listeners.<name>.ports`                 | `GATEWAY_LISTENER_<NAME>_PORTS`                   |
| `gateway.listeners.<name>.advertisedHost`        | `GATEWAY_LISTENER_<NAME>_ADVERTISED_HOST`         |
| `gateway.listeners.<name>.advertisedHostPattern` | `GATEWAY_LISTENER_<NAME>_ADVERTISED_HOST_PATTERN` |
| `gateway.listeners.<name>.bootstrapHostPattern`  | `GATEWAY_LISTENER_<NAME>_BOOTSTRAP_HOST_PATTERN`  |
| `gateway.listeners.<name>.sslClientAuth`         | `GATEWAY_LISTENER_<NAME>_SSL_CLIENT_AUTH`         |

Where `<name>` is `internal` or `external` in values, mapped to `INTERNAL` or `EXTERNAL` in the env var.

### Verify the Helm upgrade

Preview the manifest changes before applying:

```bash theme={null}
helm upgrade gateway conduktor/conduktor-gateway \
  -n conduktor -f values.yaml \
  --dry-run --debug
```

After upgrading, confirm the env vars and services are correct:

```bash theme={null}
# Check listener env vars are present (not legacy GATEWAY_PORT_*)
kubectl get deploy -n conduktor -l app.kubernetes.io/name=gateway \
  -o jsonpath='{.items[0].spec.template.spec.containers[0].env[*].name}' \
  | tr ' ' '\n' | grep GATEWAY

# Check service ports match your listener config
kubectl get svc -n conduktor -l app.kubernetes.io/name=gateway \
  -o yaml | grep -A 5 ports

# Check Gateway logs for the Computed configuration block
kubectl logs -n conduktor -l app.kubernetes.io/name=gateway --tail=50 \
  | grep -E 'Legacy|Computed configuration|LISTENER'
```

The same [verification checks](#verify-the-migration) apply — confirm the legacy deprecation warning is gone and the `Computed configuration` block lists your expected listeners.

### Clean up

After confirming the new config works, remove these legacy keys from `gateway.env` in your values file:

* `GATEWAY_SECURITY_PROTOCOL`
* `GATEWAY_ROUTING_MECHANISM`
* `GATEWAY_ADVERTISED_HOST`
* `GATEWAY_ADVERTISED_HOST_PREFIX`
* `GATEWAY_SNI_HOST_SEPARATOR`
* `GATEWAY_ADVERTISED_SNI_PORT`

And remove `gateway.portRange` entirely.

## Related resources

* [Legacy network configuration](/guide/conduktor-in-production/deploy-artifacts/deploy-gateway/legacy-network-configuration) — how the deprecated mode behaves and the full variable reference
* [Configure Gateway listeners](/guide/conduktor-in-production/deploy-artifacts/deploy-gateway/listeners)
* [Configure Gateway listener security](/guide/conduktor-in-production/deploy-artifacts/deploy-gateway/listener-security)
* [Migrate security mode (v3.10–3.19)](/guide/tutorials/migrate-gateway-security) — migrate from the deprecated `DELEGATED_*` security protocols
