# Escaping Vendor Lock-In: Kafka Portability Strategies

Your Kafka deployment works great on Confluent Cloud. Until pricing changes, or you acquire a company running MSK, or compliance requires on-prem in Frankfurt.

"Just change the bootstrap servers" becomes a six-month project.

> *We thought MSK IAM auth was convenient. Then we needed to migrate to Confluent Cloud. Every service had IAM-specific code. It took 4 months to untangle.*
>
> *Staff Engineer at a fintech*

## Where Lock-In Happens

| Layer | Portable | Vendor-Specific |
|-------|----------|-----------------|
| Wire protocol | Kafka protocol | Proprietary extensions |
| Authentication | SASL/SCRAM, mTLS | IAM (MSK), API keys (Confluent) |
| Authorization | Standard ACLs | RBAC (Confluent), IAM policies (MSK) |
| Schema Registry | Confluent OSS, Apicurio | Cloud-native registries |

The wire protocol is portable. Authentication and authorization are where portability breaks.

## Portable Configuration Pattern

Hardcoding bootstrap servers is the fastest path to lock-in:

```java
// LOCKED IN
props.put("bootstrap.servers", "pkc-abc123.confluent.cloud:9092");
```

**Portable alternative:**

```java
props.put("bootstrap.servers", System.getenv("KAFKA_BOOTSTRAP_SERVERS"));
props.put("security.protocol", System.getenv("KAFKA_SECURITY_PROTOCOL"));
props.put("sasl.mechanism", System.getenv("KAFKA_SASL_MECHANISM"));
props.put("sasl.jaas.config", System.getenv("KAFKA_SASL_JAAS_CONFIG"));
```

Same code runs against local Kafka, MSK, Confluent, or Redpanda. Only the environment changes.

## Authentication Portability

**SASL/SCRAM (Most Portable):** Works on self-managed, MSK, Aiven, Instaclustr.

**IAM Authentication (MSK Only):** This is a lock-in point.

```properties
# MSK-SPECIFIC: Won't work anywhere else
sasl.mechanism=AWS_MSK_IAM
sasl.client.callback.handler.class=software.amazon.msk.auth.iam.IAMClientCallbackHandler
```

**Portability strategy:** Use SCRAM on MSK instead of IAM if you anticipate migration. MSK supports both.

**Tradeoff:** IAM gives unified AWS identity. SCRAM gives portability. Pick one.

## The Abstraction Layer

Environment variables help with configuration. They don't help when you need to switch clusters without redeploying 47 services.

A gateway layer sits between applications and clusters. [Conduktor Gateway interceptors](https://docs.conduktor.io/guide/conduktor-concepts/interceptors) can handle authentication translation, routing, and policy enforcement across different backends.

```text
Applications (gateway.internal:9092)
           │
           ▼
    Gateway / Proxy
           │
    ┌──────┴──────┐
    ▼             ▼
Primary       Secondary
(Confluent)   (MSK)
```

Applications connect to one stable endpoint. The proxy routes traffic. Cluster migration becomes a routing change, not a coordinated deployment.

## Schema Registry Migration

Schema Registry is often overlooked as a lock-in point. Export schemas regularly:

```bash
curl -u $API_KEY:$API_SECRET \
  https://psrc-xxx.confluent.cloud/subjects | jq -r '.[]' | while read subject; do
    curl -u $API_KEY:$API_SECRET \
      "https://psrc-xxx.confluent.cloud/subjects/$subject/versions/latest/schema" \
      > schemas/$subject.avsc
done
```

Test schema compatibility on target before migration.

## Test Your Portability

Can you run your application against a local Kafka cluster with `docker-compose`? If not, you're more locked in than you think.

Vendor lock-in happens in small increments. Each proprietary convenience adds friction to your eventual migration.

[Book a demo](https://www.conduktor.io/contact/demo) to see how Conduktor Gateway, a Kafka proxy, provides the abstraction layer for multi-cloud Kafka deployments.
