Skip to main content

KafScale


December 17, 2025

Kafscale: Kafka-Compatible Streaming with Stateless Brokers and S3 Storage

Why Kafscale exists

Kafscale started three months ago when I was implementing a stateless Kafka alternative for a streaming intelligence platform. The existing solutions looked promising, but production revealed friction that compounded over time.

Control plane lock-in. Most stateless Kafka alternatives require a vendor-hosted control plane. Your agents depend on their infrastructure for consensus. For regulated industries or strict data sovereignty requirements, this external dependency was a dealbreaker.

No S3 resilience. Object storage is not infallible. When PutObject calls started timing out, brokers would retry indefinitely or fail opaquely. No backpressure, no health states, no way for operators to automate responses.

Pricing floor. Entry at $3,000/month excludes dev environments, staging clusters, and smaller workloads. We needed 100GB/day for under $150/month.

So I built it. Go for the broker runtime. gRPC and Protocol Buffers for internal communication. etcd as a self-hosted metadata store. And a proper S3 health state machine that surfaces degradation through Kafka protocol errors and Prometheus metrics.

Open source, sponsored by Scalytics.

Who Kafscale is for

Good fit: Platform teams running Kafka as plumbing. Data engineers using Kafka for CDC, log aggregation, or event sourcing with 100ms+ latency tolerance. Organizations where Kafka ops burden exceeds the value of unused features. Greenfield projects wanting Kafka compatibility without Kafka complexity.

Not a fit: Trading systems, real-time bidding, workloads requiring exactly-once semantics, compacted topics, or single-digit millisecond latency.

Architecture

Kubernetes Cluster Broker Pods (HPA) Broker 0 Broker 1 Broker 2 stateless / no local disk etcd Topic config / Offsets Consumer groups S3 Segment Storage Source of Truth .kfs .index ... flush segments fetch + cache Kafka Clients Flink / Wayang / Apps Kafka protocol Legend: data flow metadata

Brokers accept Kafka protocol connections, buffer writes, flush segments to S3, serve reads with caching and read-ahead, and coordinate consumer groups. etcd stores metadata: topic configuration, partition state, consumer group membership, and committed offsets. S3 stores immutable segment and index objects that represent the message log.

Scope

In scope

  • Kafka protocol compatibility for core producer and consumer workflows
  • Produce and fetch paths backed by immutable segment storage
  • Consumer groups, membership, heartbeats, and committed offsets
  • Topic administration needed for everyday platform use
  • Kubernetes operator integration via CRDs for cluster and topic lifecycle

Explicit non-goals

  • Exactly-once semantics and transactions
  • Compacted topics
  • Kafka internal replication and ISR protocols
  • Embedding stream processing inside the broker

Kafscale only does durable message transport. Stream processing remains the responsibility of compute engines such as Apache Flink, Apache Wayang, or any other stack that reads from Kafka topics. This keeps the broker surface area small and preserves compatibility with the Kafka ecosystem.

Storage and data model

Each topic is partitioned. Each partition is represented as an ordered sequence of immutable segment files plus a sparse index file used for offset-to-position lookup. Segment keys are based on base offsets so storage remains append-friendly and retention is handled with S3 lifecycle policies.

Topics and partitions

Topic: orders Partition 0 seg-000000.kfs seg-050000.kfs seg-100000.kfs Partition 1 seg-000000.kfs seg-050000.kfs Partition 2 seg-000000.kfs s3://bucket/namespace/orders/{partition}/segment-{offset}.kfs

S3 key layout

s3://{bucket}/{namespace}/{topic}/{partition}/segment-{base_offset}.kfs
s3://{bucket}/{namespace}/{topic}/{partition}/segment-{base_offset}.index

Segment file format

Each segment is a self-contained file with messages and metadata. The format includes a header for identification and versioning, message batches containing the actual records, and a footer with checksums for integrity verification.

Segment Header (32 bytes) Magic: 0x4B414653 ("KAFS") Version: 1 Flags Base Offset (8B) Message Count (4B) Timestamp (8B) Message Batch 1 Message Batch 2 ... Segment Footer (16 bytes) CRC32 (4B) Last Offset (8B) Magic: 0x454E4421 ("END!")

Write path

When a producer sends messages, the broker validates ownership, buffers the data, assigns offsets, and eventually flushes to S3. The acks setting controls when the producer receives confirmation.

1. RECEIVE Parse ProduceRequest Validate topic + ownership 2. BUFFER Append to write buffer Assign offsets from etcd 3. FLUSH DECISION Buffer >= 4MB? Time >= 500ms? Explicit flush? no Return to producer (acks=0 or acks=1) yes 4. BUILD SEGMENT Compress (snappy) Build header/footer + index 5. S3 UPLOAD PutObject: segment.kfs PutObject: segment.index 6. COMMIT Update etcd (HWM) Ack producers (acks=all) HWM = High Watermark

Read path

When a consumer fetches messages, the broker locates the relevant segment, checks the cache, and retrieves data from S3 if needed. Read-ahead prefetching improves performance for sequential consumers.

1. RECEIVE Parse FetchRequest 2. LOCATE SEGMENT Binary search for offset 3. CHECK CACHE LRU segment lookup hit 4a. FROM CACHE Serve cached data miss 4b. FETCH FROM S3 Load index, find position Range GET + read-ahead 5. CHECK BUFFER Add unflushed if offset > HWM 6. BUILD RESPONSE FetchResponse + HWM Legend: cache hit cache miss

S3 resiliency and backpressure

Kafscale deliberately avoids persistent local queues. When S3 misbehaves, the system surfaces it through protocol-native backpressure and operator automation instead of inventing new operational knobs.

S3 Health State Machine HEALTHY Flushes proceed normally Full read-ahead enabled Rollouts allowed HPA active latency > threshold DEGRADED Conservative retries (jittered backoff) REQUEST_TIMED_OUT when budget expires Rollouts paused Read-ahead slowed error rate > threshold UNAVAILABLE Immediate UNKNOWN_SERVER_ERROR Segment flushes disabled HPA halted, alerts fired S3Unavailable flag exposed latency recovers errors decrease Metrics kafscale_s3_health_state produce_backpressure_state s3_put_latency_ms / s3_put_failures_total Configuration KAFSCALE_S3_LATENCY_WARN_MS KAFSCALE_S3_ERROR_RATE_WARN

Every broker tracks S3 health as Healthy, Degraded, or Unavailable based on sliding-window PutObject latency and error metrics. The same health monitor wraps the fetch path so degraded buckets slow read-ahead and emit REQUEST_TIMED_OUT; unavailability raises UNKNOWN_SERVER_ERROR immediately so consumers understand the outage.

Operator guardrails

The Kubernetes operator watches broker health via control-plane RPCs or Prometheus. When any broker is Degraded, rollouts are paused. If a quorum reports Unavailable, the operator halts HPA decisions, emits alerts, and optionally rechecks IAM credentials and endpoints before resuming.

Surfacing state

The broker exposes /metrics with Prometheus-style gauges and BrokerControl.GetStatus returns a sentinel partition named __s3_health whose state field reflects the current S3 state. Operators or HPAs can watch either interface to gate rollouts or trigger alerts. For ops teams that prefer push semantics, the broker also opens a StreamMetrics gRPC stream and continuously emits the latest health snapshot plus derived latency and error stats to the operator so automation can react without scraping delays.

Consumer group protocol

Kafscale implements the standard Kafka consumer group protocol. Groups transition through states as members join, leave, or fail heartbeats. The broker handles coordination, assignment, and offset tracking.

Empty PreparingRebalance CompletingRebalance Stable first member joins all members joined leader assigns member leaves or heartbeat timeout all members leave all expire

Operational defaults

  • Bucket naming: kafscale-{environment}-{region} to isolate IAM and retention policies
  • Region affinity: bucket region matches the Kubernetes cluster region to avoid cross-region cost and latency
  • Encryption: SSE-KMS with a customer-managed CMK when provided; SSE-S3 fallback with a warning
  • Lifecycle retention: operator-managed prefix rules derived from topic retention configuration

Current development status

Kafscale is open source and under active development. We are working toward production readiness with compatibility regression testing, fault injection coverage, and repeatable benchmarks.

Full technical documentation including quickstart guides, API reference, and configuration options is available at kafscale.io. This page focuses on the architecture decisions and use cases.

How to use Kafscale in an architecture

Kafscale is intended to be used as a Kafka-compatible transport layer. Producers and consumers connect using standard Kafka client libraries. Downstream compute engines such as Flink or Wayang read from Kafscale topics using their existing Kafka connectors. The platform focus remains durable delivery and replay, not embedded processing.

Related resources

If you are evaluating Kafscale or similar architectures for your organization:

Evaluating Kafka alternatives or simplifying your streaming infrastructure?

I help teams assess streaming architectures, reduce operational burden, and design cost-effective data platforms.
See how I work with teams or book a call.

Most read articles

Building a Model-Agnostic Multi-Agent System with OpenClaw

Over one week we rebuilt our AI stack around OpenClaw’s multi-agent architecture to avoid provider lock-in and stop wasting premium tokens. By aligning models to tasks, diversifying fallbacks across providers, enforcing minimal tool access, and switching to memory-first workflows with ephemeral sessions, we reduced token usage per task by about 70% and cut our monthly bill by 77% while improving operational resilience. How We Achieved 77% Cost Reduction and Provider Independence Over the past week, we rebuilt our AI infrastructure around OpenClaw’s multi-agent architecture. The result was a 77% cost reduction , provider independence , and a delegation system that routes work to the most cost-effective model for each job. Below is the technical journey of optimizing a 7-agent squad with OpenClaw. The Challenge: Model Provider Lock-In We started with a simple problem: our entire squad defaulted to a single model provider. This created three issues: Cost inefficiency beca...

BacNet => MQTT in Production: The Real Cost of Bridging BACnet to MQTT at Scale

bacnet2mqtt looks simple in a README and expensive in production. Once BACnet polling, reconnection behavior, stale state, and MQTT publishing collide, teams discover they are not deploying a lightweight adapter but operating infrastructure. This article breaks down where bacnet2mqtt works, where it becomes a bottleneck, and which production patterns reduce the operational damage before incidents, backlogs, and silent data loss turn a building integration into a long-running engineering problem. I inherited a building controls integration problem 18 months ago. Three office floors. 217 BACnet sensors covering temperature, occupancy, and HVAC actuators. The data was trapped inside the building automation network while the business wanted analytics, reporting, and compliance visibility in the data platform. The obvious answer looked easy enough: deploy bacnet2mqtt, bridge BACnet into MQTT, and push the stream into the lakehouse stack. The repository made it sound like a w...

Get Apache Flume 1.3.x running on Windows

Since we found an increasing interest in the flume community to get Apache Flume running on Windows systems again, I spent some time to figure out how we can reach that. Finally, the good news - Apache Flume runs on Windows. You need some tweaks to get them running. Prerequisites Build system: maven 3x, git, jdk1.6.x, WinRAR (or similar program) Apache Flume agent: jdk1.6.x, WinRAR (or similar program), Ultraedit++ or similar texteditor Tweak the Windows build box 1. Download and install JDK 1.6x from Oracle 2. Set the environment variables    => Start - type " env " into the search box, select " E dit system environment variables ", click Environment Variables, Select " New " from the " Systems variables " box, type " JAVA_HOME " into " variable name " and the path to your JDK installation into "Variable value" (Example:  C:\Program Files (x86)\Java\jdk1.6.0_33 ) 3. Download maven from Apache 4. Set...