Event sourcing has emerged as one of the most powerful architectural patterns for building resilient, auditable, and scalable systems. Instead of storing only the current state of your data, event sourcing persists every state change as an immutable event in an append-only log. This gives you a complete history, natural audit trails, and the ability to replay events to rebuild state at any point in time.
But choosing the right platform to store and process those events is critical. Three options dominate the self-hosted event sourcing landscape: EventStoreDB, a purpose-built event sourcing database; Apache kafka, the industry-standard distributed event streaming platform; and Apache Pulsar, a next-generation cloud-native messaging and streaming system.
In this guide, we compare all three platforms head-to-head — covering architecture, docker deployment, performance, and real-world use cases — so you can pick the right tool for your event-driven system.
Why Self-Hosted Event Sourcing Matters
Event sourcing changes how you think about data. Instead of overwriting records, every change is appended as an event. This approach delivers several benefits that traditional CRUD architectures simply cannot match:
- Complete audit trail — Every state change is recorded, making compliance and debugging straightforward
- Temporal queries — Rebuild the state of any entity at any point in time by replaying events
- Natural integration with CQRS — Separate read and write models for optimized query performance
- Event replay capabilities — Rebuild projections, fix bugs, or create new read models from historical data
- Decoupled microservices — Services communicate through events rather than direct API calls
Running this infrastructure self-hosted gives you full control over data retention, security policies, and compliance requirements. No third-party vendor holds your event log, and you avoid the egress costs that cloud-hosted messaging platforms charge at scale.
For teams already running self-hosted message queues, extending your infrastructure with an event sourcing platform is a natural next step. If you are comparing traditional message brokers first, check out our RabbitMQ vs NATS vs ActiveMQ guide for background on the messaging layer.
EventStoreDB: Purpose-Built Event Sourcing Database
EventStoreDB (now marketed as KurrentDB) is the only database designed from the ground up specifically for event sourcing. It treats events as first-class citizens, with native support for streams, projections, and event replay.
Key Features
- Event streams — Each aggregate gets its own stream of events, identified by a unique stream ID
- Projections — Transform event streams into read models using JavaScript-based projection functions
- Optimistic concurrency — Prevents write conflicts with version checking per stream
- Catch-up subscriptions — Subscribe to a stream from any version, including live updates
- Built-in web UI — Monitor streams, projections, and cluster health through a browser
- gRPC and TCP APIs — Native clients for .NET, Java, Go, JavaScript, and Python
- Cluster support — Raft-based consensus for high-availability deployments
Project Stats (as of April 2026)
| Metric | Value |
|---|---|
| GitHub Stars | 5,776 |
| Primary Language | C# |
| License | BSL 1.1 (Business Source License) |
| Last Commit | April 18, 2026 |
| Docker Image | eventstore/eventstore |
Docker Deployment
EventStoreDB’s official Docker Compose sets up a three-node cluster with TLS certificates and gossip-based discovery:
| |
For a single-node development setup, the command is much simpler:
| |
The admin UI runs on port 2113. Access http://localhost:2113 with default credentials admin/changeit.
Apache Kafka: The Industry Standard
Apache Kafka is the most widely adopted event streaming platform. Originally built at LinkedIn, it has become the backbone of event-driven architectures at thousands of organizations. While not designed specifically for event sourcing, Kafka’s append-only log model maps naturally to event sourcing patterns.
Key Features
- Topic-based partitions — Horizontally scalable logs with ordered message delivery per partition
- Consumer groups — Multiple independent consumers can read the same topic at their own pace
- Log compaction — Retains the latest value per key, enabling event-sourced state reconstruction
- Kafka Streams — Built-in stream processing library for real-time transformations
- Connect API — Hundreds of pre-built connectors for databases, file systems, and SaaS platforms
- Schema Registry — Enforce schema compatibility with Avro, Protobuf, or JSON Schema
- Tiered storage — Offload older segments to S3 or GCS for cost-effective long-term retention
Project Stats (as of April 2026)
| Metric | Value |
|---|---|
| GitHub Stars | 32,414 |
| Primary Language | Java |
| License | Apache 2.0 |
| Last Commit | April 18, 2026 |
| Docker Image | apache/kafka |
Docker Deployment
The Confluent Platform provides a production-ready Docker Compose for Kafka with ZooKeeper:
| |
For a ZooKeeper-free deployment using KRaft mode (Kafka 3.x+):
| |
Apache Pulsar: Cloud-Native Messaging and Streaming
Apache Pulsar, originally developed at Yahoo, is a next-generation distributed messaging and streaming platform. It separates compute (brokers) from storage (bookies using Apache BookKeeper), enabling independent scaling and multi-tenancy — capabilities that make it uniquely suited for large-scale event sourcing deployments.
Key Features
- Native multi-tenancy — Tenants, namespaces, and topics with per-namespace policies and quotas
- Unified messaging and streaming — Both queuing (ack-based) and streaming (cursor-based) models
- Tiered storage — Automatic offloading to S3, GCS, or HDFS with transparent read-through
- Built-in functions — Lightweight compute framework for event processing without external frameworks
- Geo-replication — Cross-cluster replication with conflict resolution
- Schema registry — Native schema management with versioning and compatibility checks
- Functions, Sinks, Sources — Built-in connectors for data ingestion and egress
Project Stats (as of April 2026)
| Metric | Value |
|---|---|
| GitHub Stars | 15,203 |
| Primary Language | Java |
| License | Apache 2.0 |
| Last Commit | April 17, 2026 |
| Docker Image | apachepulsar/pulsar |
Docker Deployment
Pulsar’s standalone mode is ideal for development and testing:
| |
For a production cluster with BookKeeper, use Docker Compose:
| |
Head-to-Head Comparison
| Feature | EventStoreDB | Apache Kafka | Apache Pulsar |
|---|---|---|---|
| Primary Design | Event sourcing database | Event streaming platform | Messaging + streaming |
| Event Model | Streams per aggregate | Topic partitions | Persistent topics |
| Storage | Append-only log on disk | Log segments on disk | BookKeeper ledgers |
| Message Ordering | Per-stream guaranteed | Per-partition guaranteed | Per-partition guaranteed |
| Retention | Configurable per stream | Time or size based | Time, size, or infinite |
| Replay Support | Native (from any version) | From offset | From cursor position |
| Projections | Built-in (JavaScript) | Kafka Streams required | Functions/Pulsar IO |
| Multi-tenancy | Via access controls | Via ACLs | Native tenants/namespaces |
| Geo-replication | Via subscriptions | MirrorMaker 2 | Built-in |
| Schema Registry | Via metadata | Confluent Schema Registry | Built-in |
| Admin UI | Built-in web UI | CMAK, Confluent Control Center | Built-in dashboard |
| Protocol | gRPC, TCP | Custom binary | Pulsar protocol |
| Client Languages | .NET, Java, Go, JS, Python | Java, Python, Go, Rust, C++ | Java, Python, Go, C++, Node.js |
| License | BSL 1.1 | Apache 2.0 | Apache 2.0 |
| GitHub Stars | 5,776 | 32,414 | 15,203 |
When to Choose Each Platform
Choose EventStoreDB when:
- You are implementing CQRS with event sourcing as the core architectural pattern
- You need per-stream optimistic concurrency control
- You want built-in projections to generate read models
- Your domain model maps naturally to aggregate streams
- You need temporal queries (state at a specific point in time)
Choose Apache Kafka when:
- You need the broadest ecosystem of connectors, tools, and community support
- You are already using Kafka for event streaming and want to add event sourcing
- You need high-throughput, low-latency event processing at massive scale
- Log compaction and Schema Registry meet your event sourcing needs
- Your team has existing Kafka expertise
Choose Apache Pulsar when:
- You need native multi-tenancy for serving multiple teams or customers
- Tiered storage to object storage is a requirement for long-term event retention
- You want unified queuing and streaming semantics in one platform
- You need geo-replication with conflict resolution out of the box
- Independent scaling of compute and storage matters for your workload
Event Sourcing Implementation Patterns
CQRS with EventStoreDB
| |
Event Sourcing with Kafka and Log Compaction
Kafka’s log compaction feature keeps the latest record for each message key, enabling event-sourced state reconstruction:
| |
Multi-Tenant Event Sourcing with Pulsar
Pulsar’s tenant/namespace model provides clean separation for event sourcing across multiple services:
| |
Performance Considerations
| Metric | EventStoreDB | Apache Kafka | Apache Pulsar |
|---|---|---|---|
| Write Throughput | ~50K events/sec (single node) | ~1M+ msgs/sec (cluster) | ~100K+ msgs/sec (cluster) |
| Read Latency | Sub-millisecond (per stream) | Low (sequential read) | Low (BookKeeper read) |
| Storage Efficiency | High (append-only) | High (log segments) | Medium (ledger overhead) |
| Scalability | Vertical (stream affinity) | Horizontal (partitions) | Horizontal (brokers + bookies) |
| Recovery Time | Fast (single log per stream) | Fast (sequential replay) | Fast (cursor-based replay) |
EventStoreDB excels when you need precise per-stream operations with optimistic concurrency. Kafka delivers the highest raw throughput for fire-and-forget event streaming. Pulsar offers the best operational flexibility with compute-storage separation.
If you are building complex workflows on top of your event streams, you may want to pair your event sourcing platform with a workflow orchestration engine. See our Temporal vs Camunda vs Flowable comparison for guidance on that layer.
FAQ
What is event sourcing and how does it differ from traditional CRUD?
Event sourcing stores every state change as an immutable event in an append-only log, rather than overwriting the current state. This means you have a complete history of all changes, can rebuild state at any point in time, and get natural audit trails. Traditional CRUD only stores the current state, losing all historical information.
Can I use Kafka for event sourcing?
Yes. Kafka’s partitioned, ordered, append-only log maps well to event sourcing patterns. Using log compaction (cleanup.policy=compact), you can retain the latest state per aggregate key while keeping the full event history. However, Kafka lacks native per-stream concurrency control and projections, which EventStoreDB provides out of the box.
Which platform is easiest to self-host?
For a single-node development setup, all three are straightforward with Docker. EventStoreDB requires just one docker run command. Pulsar standalone mode is similarly simple. Kafka’s KRaft mode eliminates the ZooKeeper dependency, making it easier than before. For production clusters, Pulsar’s compute-storage separation gives you the most operational flexibility.
How do I handle schema evolution with event sourcing?
All three platforms support schema evolution through different mechanisms. EventStoreDB stores event type names as metadata, allowing consumers to handle multiple versions. Kafka pairs with Confluent Schema Registry for Avro/Protobuf compatibility enforcement. Pulsar has a built-in schema registry with version tracking. The key principle: make events append-only — never modify existing events, only add new ones.
Is EventStoreDB truly open source?
EventStoreDB uses the Business Source License (BSL 1.1), which allows free use for development, testing, and production — but restricts offering EventStoreDB as a managed service. After a set period, each release converts to the Apache 2.0 license. Apache Kafka and Apache Pulsar are both Apache 2.0 licensed with no such restrictions.
How do I handle large event stores with millions of events?
All three platforms support long-term retention. Kafka and Pulsar both offer tiered storage to offload older segments to S3 or GCS while keeping them readable. EventStoreDB uses scavenge operations to reclaim space from deleted or truncated streams. For projections, materialize derived state into a separate read database (PostgreSQL, Elasticsearch) to avoid replaying millions of events on every query.
Can I migrate between these platforms?
Yes, but it requires careful planning. The common approach is to build an adapter that reads events from the source platform and writes them to the target, preserving ordering and metadata. For Kafka to EventStoreDB migrations, write each Kafka message as an event to the corresponding stream. For EventStoreDB to Kafka, subscribe to all streams and publish each event to a Kafka topic partitioned by stream ID.