Connectivity and emulator mode
How a Google Pub/Sub SDK reaches the connector — the PUBSUB_EMULATOR_HOST drop-in, per-language emulator opt-in, insecure gRPC, and the cluster caveat.
This guide covers how a Google Cloud Pub/Sub client reaches the connector: the standard
PUBSUB_EMULATOR_HOST drop-in (the zero-code-change contract), the per-language emulator opt-in,
the no-auth / insecure-gRPC posture, the cosmetic AdvertisedEndpoint dashboard hint, the
gRPC-only constraint, and the sticky-load-balancer caveat for clusters.
The emulator protocol — zero code changes
The connector is a dedicated gRPC listener (default port 8085, the Pub/Sub emulator
convention) inside KubeMQ. Every official Pub/Sub client library (Go, Python, Java, Node.js, C#,
Ruby) and gcloud honour the standard PUBSUB_EMULATOR_HOST environment variable. When that
variable is set, the SDK clears its Google credentials, skips Google auth, and dials insecure
gRPC — exactly as it would against Google's own local emulator. Pointing an unmodified Pub/Sub
application at KubeMQ therefore requires no code changes:
export PUBSUB_EMULATOR_HOST=localhost:8085 # connector default gRPC port; SDK uses the insecure path
export PUBSUB_PROJECT_ID=my-project # any id; the project segment is parsed but ignored
# Some clients and gcloud also read this alias:
# export GOOGLE_CLOUD_PROJECT=my-projectThere is no bespoke wrapper variable — the standard Google env var is the contract, and that zero-code-change drop-in is the connector's headline value proposition.
Project id is parsed but ignored. The connector validates the projects/{p} segment but is
single-tenant (like the emulator): resource ids are global across "projects". Any project id
works; topic orders is always Events Store channel gcp.orders regardless of project. See
Channel mapping.
Per-language emulator opt-in
Most clients auto-detect the emulator from PUBSUB_EMULATOR_HOST with no extra code; two need an
explicit flag or constructor argument:
| Language | Construction (emulator) | Auto-detect? |
|---|---|---|
| Go | pubsub.NewClient(ctx, projectID) — reads PUBSUB_EMULATOR_HOST and dials insecurely. | Yes |
| Python | pubsub_v1.PublisherClient() / SubscriberClient() — honours the env var. | Yes |
| Node/TS | new PubSub({ projectId }) — auto-detects the emulator from the env var. | Yes |
| Java | Point a plaintext ManagedChannel at the emulator host with NoCredentialsProvider when PUBSUB_EMULATOR_HOST is set. | No — explicit channel |
| C# | new PublisherServiceApiClientBuilder { EmulatorDetection = EmulatorDetection.EmulatorOnly }.Build() (or EmulatorOrProduction). | No — EmulatorDetection |
| Ruby | Google::Cloud::PubSub.new(project_id: ENV["PUBSUB_PROJECT_ID"], emulator_host: ENV["PUBSUB_EMULATOR_HOST"]). | No — emulator_host: |
The three clients that need explicit emulator wiring are Java, C#, and Ruby:
// When PUBSUB_EMULATOR_HOST is set, build a plaintext channel and clear credentials.
String host = System.getenv("PUBSUB_EMULATOR_HOST"); // e.g. localhost:8085
ManagedChannel channel = ManagedChannelBuilder.forTarget(host).usePlaintext().build();
TransportChannelProvider channelProvider =
FixedTransportChannelProvider.create(GrpcTransportChannel.create(channel));
CredentialsProvider credentialsProvider = NoCredentialsProvider.create();
TopicAdminClient topicAdmin = TopicAdminClient.create(
TopicAdminSettings.newBuilder()
.setTransportChannelProvider(channelProvider)
.setCredentialsProvider(credentialsProvider)
.build());// EmulatorDetection reads PUBSUB_EMULATOR_HOST and switches to the insecure path.
var publisher = await new PublisherServiceApiClientBuilder
{
EmulatorDetection = EmulatorDetection.EmulatorOnly
}.BuildAsync();# Ruby does not auto-detect the emulator — pass emulator_host: explicitly.
require "google/cloud/pubsub"
pubsub = Google::Cloud::PubSub.new(
project_id: ENV["PUBSUB_PROJECT_ID"],
emulator_host: ENV["PUBSUB_EMULATOR_HOST"], # e.g. localhost:8085
)No auth, no TLS
The connector runs in emulator mode: no Google OAuth2/JWT validation, no IAM enforcement (the
IAMPolicy RPCs are permissive stubs), and no TLS. The transport is insecure gRPC.
Do not expose port 8085 to untrusted networks. This no-auth, no-TLS posture is by design and matches Google's local emulator. Run it on a trusted network or behind your own perimeter. For the shared security model across connectors, see Auth & security.
DoS guards remain active even with no auth:
CONNECTORS_GCP_MAX_MESSAGE_BYTES— a gRPC receive-size body cap (default 10 MiB);CONNECTORS_GCP_MAX_INFLIGHT_PER_SUBSCRIPTION— hard cap on leased (un-acked) messages per subscription (20,000);CONNECTORS_GCP_MAX_CONCURRENT_POLLS— poller slot budget (1,024);CONNECTORS_GCP_MAX_SEEK_REPLAY— max messages a singleSeekmay replay (1,000,000);- push-delivery backoff.
These knobs are documented in Configuration.
The advertised-endpoint hint
CONNECTORS_GCP_ADVERTISED_ENDPOINT (default empty) is a cosmetic host:port string the
dashboard shows as the suggested PUBSUB_EMULATOR_HOST value. It does not change how the listener
binds or behaves; set it to the externally reachable address (e.g. kubemq.mycompany.svc:8085) so
operators copy the right host into their env.
gRPC only — no REST/JSON
The connector is gRPC-only. There is no REST/JSON v1 (grpc-gateway) surface. Clients and
tools that speak only the Pub/Sub REST API will not work; use a gRPC client library or gcloud
(which uses gRPC for the emulator). See
Capabilities.
Periodic stream reconnect
A StreamingPull stream is closed by the server after CONNECTORS_GCP_STREAM_CLOSE_SECONDS
(default 1800 s / 30 min) with UNAVAILABLE; client libraries transparently reconnect.
This bounds per-stream resource lifetime and is normal — your receive callback keeps running across
the reconnect. See Subscribing.
Sticky-stream caveat (cluster)
Node-local state needs a sticky load balancer. Topic / subscription / snapshot / schema records are synchronized across cluster nodes (a per-node replicated registry, last-writer-wins), but two pieces of delivery state are node-local:
- Exactly-once
ack_ids — anack_idminted on one node is invalid on another (its node id won't match), so an exactly-once subscription'sStreamingPulltraffic must be pinned to one node; StreamingPullleases / in-flight tracking — leased messages and flow-control counters live on the node that delivered them.
Cluster deployments must put a sticky load balancer (session affinity) in front of the connector so each subscriber sticks to one node for the lifetime of its in-flight messages. Single-node deployments are unaffected. See Reliability.
Message data itself is not replicated by the connector — it rides the existing Events Store / Queues replication.
Traffic gate
While the message broker is not ready, the traffic-gate interceptor short-circuits requests; on a
not-ready → ready transition the connector drops all in-memory leases (their downstream
transactions are dead) and the poller rebuilds. SDKs see a transient UNAVAILABLE and retry.
Related
Configuration
The opt-in CONNECTORS_GCP_ENABLE flag, gRPC port 8085, and the thirteen CONNECTORS_GCP_* settings and their defaults.
Subscribing
Pull vs StreamingPull, the ack-deadline lease, flow control, and the periodic reconnect in depth.
Auth & security
The shared security model across KubeMQ connectors, including TLS via the server Security block.
Was this page helpful?
Getting Started
Connect a Google Cloud Pub/Sub SDK to KubeMQ on gRPC port 8085 via PUBSUB_EMULATOR_HOST, then run a publish, pull, and acknowledge round-trip in minutes.
Fan-Out
One publish, many subscriptions over KubeMQ — fan a Pub/Sub topic message out to independent subscriptions on gcp.sub.{s} queues, each with its own ack state.