Event Hubs
Introduction
Section titled “Introduction”Azure Event Hubs is a real-time data streaming platform that ingests and processes millions of events per second. Producers append events to partitioned logs, and consumers read those logs independently at their own pace, which makes Event Hubs a common backbone for telemetry pipelines, event sourcing, and stream analytics. It exposes its data plane over AMQP 1.0, HTTPS, and a Kafka-compatible endpoint. For more information, see What is Azure Event Hubs?
LocalStack for Azure provides a local environment for building and testing applications that make use of Azure Event Hubs. The supported APIs are available on our API Coverage section, which provides information on the extent of Event Hubs’ integration with LocalStack.
Getting started
Section titled “Getting started”This guide is designed for users new to Event Hubs and assumes basic knowledge of the Azure CLI and our lstk az proxy.
Launch LocalStack using your preferred method. For more information, see Introduction to LocalStack for Azure. Once the container is running, enable Azure CLI interception by running:
lstk az start-interceptionThis command points the az CLI away from the public Azure management REST API and toward the LocalStack for Azure emulator API.
To revert this configuration, run:
lstk az stop-interceptionThis reconfigures the az CLI to send commands to the official Azure management REST API.
Create a resource group
Section titled “Create a resource group”Create a resource group to contain your Event Hubs resources:
az group create \ --name rg-eventhubs-demo \ --location westeurope{ "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-eventhubs-demo", "location": "westeurope", "managedBy": null, "name": "rg-eventhubs-demo", "properties": { "provisioningState": "Succeeded" }, "tags": null, "type": "Microsoft.Resources/resourceGroups"}Create an Event Hubs namespace
Section titled “Create an Event Hubs namespace”Create an Event Hubs namespace in the resource group:
az eventhubs namespace create \ --resource-group rg-eventhubs-demo \ --name ehnsdoc42 \ --location westeurope \ --sku Standard{ "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-eventhubs-demo/providers/Microsoft.EventHub/namespaces/ehnsdoc42", "name": "ehnsdoc42", "location": "westeurope", "kafkaEnabled": true, "provisioningState": "Succeeded", "serviceBusEndpoint": "https://ehnsdoc42.servicebus.azure.localhost.localstack.cloud:4511", "sku": { "capacity": 1, "name": "Standard", "tier": "Standard" }, "status": "Active", ...}Create an event hub
Section titled “Create an event hub”Create an event hub in the namespace:
az eventhubs eventhub create \ --resource-group rg-eventhubs-demo \ --namespace-name ehnsdoc42 \ --name orders-hub \ --partition-count 4 \ --cleanup-policy Delete \ --retention-time 168{ "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-eventhubs-demo/providers/Microsoft.EventHub/namespaces/ehnsdoc42/eventhubs/orders-hub", "name": "orders-hub", "location": "westeurope", "messageRetentionInDays": 7, "partitionCount": 4, "partitionIds": [ "0", "1", "2", "3" ], "retentionDescription": { "cleanupPolicy": "Delete", "retentionTimeInHours": 168 }, "status": "Active", "type": "Microsoft.EventHub/namespaces/eventhubs", ...}Create a consumer group
Section titled “Create a consumer group”Create a consumer group for the event hub:
az eventhubs eventhub consumer-group create \ --resource-group rg-eventhubs-demo \ --namespace-name ehnsdoc42 \ --eventhub-name orders-hub \ --name analytics{ "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-eventhubs-demo/providers/Microsoft.EventHub/namespaces/ehnsdoc42/eventhubs/orders-hub/consumergroups/analytics", "name": "analytics", "type": "Microsoft.EventHub/namespaces/eventhubs/consumergroups", ...}List the consumer groups of the event hub:
az eventhubs eventhub consumer-group list \ --resource-group rg-eventhubs-demo \ --namespace-name ehnsdoc42 \ --eventhub-name orders-hub \ --query "[].name" -o json[ "$Default", "analytics"]Manage authorization rules and connection strings
Section titled “Manage authorization rules and connection strings”Create a send-only authorization rule on the event hub:
az eventhubs eventhub authorization-rule create \ --resource-group rg-eventhubs-demo \ --namespace-name ehnsdoc42 \ --eventhub-name orders-hub \ --name send-policy \ --rights Send{ "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-eventhubs-demo/providers/Microsoft.EventHub/namespaces/ehnsdoc42/eventhubs/orders-hub/authorizationRules/send-policy", "name": "send-policy", "rights": [ "Send" ], "type": "Microsoft.EventHub/namespaces/eventhubs/authorizationrules"}List the connection strings for the rule:
az eventhubs eventhub authorization-rule keys list \ --resource-group rg-eventhubs-demo \ --namespace-name ehnsdoc42 \ --eventhub-name orders-hub \ --name send-policy{ "keyName": "send-policy", "primaryConnectionString": "Endpoint=sb://ehnsdoc42.servicebus.azure.localhost.localstack.cloud:4511/;SharedAccessKeyName=send-policy;SharedAccessKey=...;UseDevelopmentEmulator=true;EntityPath=orders-hub", "primaryKey": "...", "secondaryConnectionString": "Endpoint=sb://ehnsdoc42.servicebus.azure.localhost.localstack.cloud:4511/;SharedAccessKeyName=send-policy;SharedAccessKey=...;UseDevelopmentEmulator=true;EntityPath=orders-hub", "secondaryKey": "..."}The namespace-level RootManageSharedAccessKey rule is created automatically with the namespace:
az eventhubs namespace authorization-rule keys list \ --resource-group rg-eventhubs-demo \ --namespace-name ehnsdoc42 \ --name RootManageSharedAccessKey{ "keyName": "RootManageSharedAccessKey", "primaryConnectionString": "Endpoint=sb://ehnsdoc42.servicebus.azure.localhost.localstack.cloud:4511/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=...;UseDevelopmentEmulator=true", ...}Send events over HTTP
Section titled “Send events over HTTP”The Event Hubs runtime REST API accepts events on the namespace endpoint. Send a single event to the event hub:
curl -sk -i -X POST \ "https://ehnsdoc42.servicebus.azure.localhost.localstack.cloud:4566/orders-hub/messages" \ -H "Authorization: SharedAccessSignature sr=...&sig=...&se=...&skn=RootManageSharedAccessKey" \ -H "Content-Type: application/json" \ -d '{"orderId": 1001, "status": "created"}'HTTP/2 201server: TwistedWeb/26.4.0content-type: application/xml; charset=utf-8content-length: 0Route an event to a specific partition by partition key using the BrokerProperties header:
curl -sk -i -X POST \ "https://ehnsdoc42.servicebus.azure.localhost.localstack.cloud:4566/orders-hub/messages" \ -H "Authorization: SharedAccessSignature sr=...&sig=...&se=...&skn=RootManageSharedAccessKey" \ -H 'BrokerProperties: {"PartitionKey": "customer-42"}' \ -H "Content-Type: application/json" \ -d '{"orderId": 1002, "status": "created"}'HTTP/2 201server: TwistedWeb/26.4.0content-type: application/xml; charset=utf-8content-length: 0Produce and consume events over Kafka
Section titled “Produce and consume events over Kafka”The namespace endpoint also serves the Kafka protocol.
Configure a Kafka client exactly as for Azure: SASL_SSL with the PLAIN mechanism, the literal username $ConnectionString, and the namespace connection string as the password.
For more information, see Azure Event Hubs for Apache Kafka.
The bootstrap server is the host and port of the connection string’s endpoint; an Azure connection string carries no port, so the script below falls back to 9093, the Kafka port in Azure.
Read the namespace connection string into an environment variable:
export EVENTHUB_CONNECTION_STRING=$(az eventhubs namespace authorization-rule keys list \ --resource-group rg-eventhubs-demo \ --namespace-name ehnsdoc42 \ --name RootManageSharedAccessKey \ --query primaryConnectionString \ --output tsv)Install the confluent-kafka client library for Python:
pip install confluent-kafkaSave the following code as kafka_orders.py.
It produces an event to the event hub, then reads the event hub from the beginning:
import osfrom urllib.parse import urlparse
from confluent_kafka import Consumer, Producer
connection_string = os.environ["EVENTHUB_CONNECTION_STRING"]endpoint = urlparse(connection_string.split(";")[0].removeprefix("Endpoint="))config = { "bootstrap.servers": f"{endpoint.hostname}:{endpoint.port or 9093}", "security.protocol": "SASL_SSL", "sasl.mechanism": "PLAIN", "sasl.username": "$ConnectionString", "sasl.password": connection_string,}
def on_delivery(err, msg): if err is not None: raise SystemExit(f"the event was not delivered: {err}")
producer = Producer(config)producer.produce( "orders-hub", value=b'{"orderId": 1003, "status": "created"}', on_delivery=on_delivery,)if producer.flush(30): raise SystemExit("the event was not delivered")
consumer = Consumer({ **config, "group.id": "kafka-demo", "auto.offset.reset": "earliest", "enable.auto.commit": False,})consumer.subscribe(["orders-hub"])while (message := consumer.poll(timeout=15)) is not None: if message.error(): raise SystemExit(message.error()) print(message.value().decode())consumer.close()Run it:
python kafka_orders.py{"orderId": 1001, "status": "created"}{"orderId": 1002, "status": "created"}{"orderId": 1003, "status": "created"}The consumer receives the two events sent over HTTP in the previous step as well as the one produced over Kafka, because all protocols share one log. Events from different partitions can arrive in a different order.
Capture events to Blob Storage
Section titled “Capture events to Blob Storage”Event Hubs Capture archives the event stream into a storage account as Avro files. Create a storage account for the archives:
az storage account create \ --resource-group rg-eventhubs-demo \ --name stehcapturels \ --location westeurope \ --sku Standard_LRS{ "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-eventhubs-demo/providers/Microsoft.Storage/storageAccounts/stehcapturels", "name": "stehcapturels", "kind": "StorageV2", "location": "westeurope", "primaryEndpoints": { "blob": "https://stehcapturels.blob.core.azure.localhost.localstack.cloud:4566", ... }, "provisioningState": "Succeeded", ...}Enable Capture on the event hub:
STORAGE_ID=$(az storage account show \ --resource-group rg-eventhubs-demo \ --name stehcapturels \ --query id -o tsv)
az eventhubs eventhub update \ --resource-group rg-eventhubs-demo \ --namespace-name ehnsdoc42 \ --name orders-hub \ --enable-capture true \ --capture-interval 60 \ --capture-size-limit 10485760 \ --destination-name EventHubArchive.AzureBlockBlob \ --storage-account "$STORAGE_ID" \ --blob-container eventhub-capture \ --skip-empty-archives true{ "captureDescription": { "destination": { "blobContainer": "eventhub-capture", "name": "EventHubArchive.AzureBlockBlob", "storageAccountResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-eventhubs-demo/providers/Microsoft.Storage/storageAccounts/stehcapturels" }, "enabled": true, "encoding": "Avro", "intervalInSeconds": 60, "sizeLimitInBytes": 10485760, "skipEmptyArchives": true }, "name": "orders-hub", ...}Send a few more events, wait for the capture window to elapse (60 seconds here), and list the archives:
STORAGE_CONNECTION=$(az storage account show-connection-string \ --resource-group rg-eventhubs-demo \ --name stehcapturels \ --query connectionString -o tsv)
az storage blob list \ --container-name eventhub-capture \ --connection-string "$STORAGE_CONNECTION" \ --query "[].{name:name, bytes:properties.contentLength}" -o json[ { "bytes": 605, "name": "ehnsdoc42/orders-hub/0/2026/08/09/19/23/32.avro" }, { "bytes": 674, "name": "ehnsdoc42/orders-hub/1/2026/08/09/19/23/32.avro" }, { "bytes": 605, "name": "ehnsdoc42/orders-hub/2/2026/08/09/19/23/32.avro" }, { "bytes": 605, "name": "ehnsdoc42/orders-hub/3/2026/08/09/19/23/32.avro" }]The archives are Avro object-container files in Azure’s EventData record schema, one per partition and capture window.
After each archive is written, the emulator raises the Microsoft.EventHub.CaptureFileCreated event through Event Grid system topics, so downstream automation such as an Azure Function can react to new archives.
Features
Section titled “Features”The emulator includes the following core capabilities:
- Multi-protocol data plane on a single event log: AMQP 1.0 (what the official
azure-eventhubSDKs speak, including AMQP-over-WebSockets), a Kafka-compatible endpoint, the HTTP runtime API, and the legacy Atom-XML management API all read and write the same per-partition log, so an event sent over one protocol is readable over the others. - ARM control plane: CRUD for namespaces, event hubs, consumer groups, authorization rules and keys, network rule sets, schema groups, application groups, geo-disaster-recovery configurations, and dedicated clusters — with tier quotas enforced as in Azure (event hub counts, retention caps, partition limits, and the Basic-tier consumer-group restriction).
- Event Hubs Capture: per-partition Avro archives written to emulated Blob Storage on time- or size-based windows, including
skipEmptyArchives, and theMicrosoft.EventHub.CaptureFileCreatedsystem event raised through Event Grid. - Schema Registry: the data-plane API served on the namespace endpoint, with schema group CRUD, schema registration and versioning, lookup by content, and the tier quota on schema groups.
- Checkpointing: the SDK’s event processor pattern works with the blob-backed checkpoint store against emulated Blob Storage.
- Kafka-compatible endpoint: topics map to event hubs, Kafka offsets equal Event Hubs sequence numbers, and consumer-group offsets are stored broker-side; verified with
kafka-pythonandconfluent-kafka. Clients use the settings Azure requires (security.protocol=SASL_SSL,sasl.mechanism=PLAIN,sasl.username=$ConnectionString, and the connection string assasl.password), with the bootstrap server read from the emulator’s connection string;SASL_SSLis verified withconfluent-kafka. - Emulator-ready connection strings:
keys listreturns connection strings that the Azure SDKs, Kafka clients, and IaC tools can use verbatim against the emulator.
Limitations
Section titled “Limitations”The current version of the emulator does not support the following:
- Credential verification: SAS tokens, JWTs, and Kafka SASL credentials are checked for the existence of the target namespace or entity, but signatures and keys are not verified. The Kafka endpoint also accepts plaintext,
SSLwithout SASL, andSASL_PLAINTEXTclients, which Azure rejects. - Microsoft Entra ID on the Kafka endpoint: only SASL
PLAINwith a connection string is supported, so Kafka clients that authenticate withOAUTHBEARERare rejected withUNSUPPORTED_SASL_MECHANISM. - Azure’s partition-key hash: the partition-key-to-partition mapping is a stable hash, so events with the same key land on the same partition, but the concrete partition ids differ from real Azure.
- Kafka namespace isolation: Kafka topics resolve across all Kafka-enabled namespaces, and Kafka consumer-group offsets and producer ids are held in memory only.
- Enforcement of throughput and network controls: throughput-unit throttling, application-group policies, network rule sets, private endpoints, and geo-disaster-recovery are stored and echoed as metadata, but not enforced — a geo-DR failover changes state without moving events.
- Dedicated cluster hardware: clusters are metadata-level, and Azure’s 4-hour cluster deletion moratorium is intentionally skipped so local clean-up is immediate.
- Receiving over HTTP: the HTTP runtime API is send-only, as in Azure; receiving requires AMQP or Kafka.
- Persistence: entities and event data are held in memory and are lost when the emulator restarts. Recreate resources on startup via the CLI or IaC.
Samples
Section titled “Samples”Explore the following samples to get started with Event Hubs on LocalStack:
- Payment fraud detection pipeline with Event Hubs, Functions, and Capture
- Cold-path automation with Event Hubs Capture, Event Grid, and Functions
- Event-driven autoscaling on AKS with KEDA and Event Hubs
API Coverage
Section titled “API Coverage”68 of 74 operations implemented
| Operation ▲ | Implemented ▼ |
|---|