Eventuate Local: event sourcing on MySQL and Kafka, on your own machine
The on premise, open source version of Eventuate, which is a platform for developing transactional business applications that use the microservice architecture.
At a glance
- What is it?
- Eventuate Local is the on premise, open source edition of Eventuate, giving Java teams an event driven programming model based on event sourcing and CQRS with the same API as the hosted version, but persisting events in a SQL database and using Kafka for publish and subscribe. It suits a team that wants Eventuate's model without a SaaS dependency, and it is a poor fit if you want a managed platform rather than infrastructure you operate.
- Who is it for?
- Eventuate Local suits a team that wants the Eventuate programming model, with the same API as the hosted product, but running on infrastructure it controls, especially where the event store must live in a database you already back up and audit.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Java, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Event sourcing with a SQL event store and Kafka for fan out
Eventuate Local is the on premise, open source version of Eventuate, which is a platform for developing transactional business applications that use the microservice architecture.
The model underneath is stated directly: Eventuate provides an event-driven programming model for microservices that is based on event sourcing and CQRS. Event sourcing means the events are the source of truth and the current state is derived from them, and CQRS means the write side and the read side are separate.
What makes the local edition interesting is stated in one sentence: it has the same API as the SaaS version but uses a SQL database to persist events and Kafka as the publish and subscribe mechanism.
So the architecture is two parts. A framework for developing microservice based applications, and an event store consisting of a SQL database, currently MySQL, and Kafka. The framework persists events in an EVENTS table in the SQL database and subscribes to events in Kafka.
That is a meaningfully different deployment from the hosted product, and it is the reason to use the local edition at all: the database is one you can query, back up and put under your existing retention rules, rather than an API you call over a network.
The project is written in Java, the last push to the repository was on 2026-10-22, and there are no GitHub releases published, so the artifacts come from Maven Central rather than from release tags. The README shows a Maven Central badge for the Spring and Micronaut BOM and a separate one for the Quarkus BOM, which tells you the framework is delivered as a family of artifacts rather than a single jar.
Change data capture is the hinge the whole design turns on
The mechanism that ties the database to the message broker is described in the About change data capture section, and it is worth understanding because it explains both the strengths and the constraints.
Eventuate Local has a change data capture component that tails the MySQL transaction log and publishes each event inserted into the EVENTS table to the Kafka topic for the event's aggregate.
So the write path is deliberately simple. Your application writes an event into the EVENTS table inside its normal database transaction, and it never publishes to Kafka itself. The CDC component reads the transaction log, notices the new rows, and publishes them.
The benefits of that arrangement are real. Because the event write and the state change are in the same transaction, there is no window where a message is published for a change that later rolls back, which is the classic dual write problem. There is also no publisher to configure, no outbox pattern to implement, and no code in the application that knows Kafka exists.
The costs are equally real, and they follow from the same choice. The CDC component must be able to read the MySQL transaction log, which constrains the MySQL version and the configuration, and it introduces a component that has to be deployed and monitored. The README says it runs either embedded within each application or as a service called cdcservice, so the embedded option keeps the topology small while the service option keeps a single tailer for the whole system.
There is also a Kafka topic for each aggregate type, which is the next piece worth understanding.
One Kafka topic per aggregate type, and the tables you need
The topic layout is stated as a single line: there is a Kafka topic for each aggregate type.
That design follows from the event sourcing model. Consumers that care about CustomerRegisteredEvent subscribe to the customer topic and never see the order events, and a consumer that needs everything subscribes to the pattern rather than to one topic. The cost is a growing topic count, and the operational work of keeping topics aligned with the aggregate types in your model.
The setup section lists what you need before the framework is usable.
First, create EVENT and ENTITIES tables in a MySQL database. The README writes them as EVENT and ENTITIES here, while the framework section refers to the EVENTS table where the events are persisted, so the exact table names in the schema are worth checking against the Docker image rather than assuming.
Second, run Apache Zookeeper, Apache Kafka and the optional cdcservice. Third, use the Eventuate Local artifacts in your application.
The order of those three steps matters more than it looks, because the CDC component reads the database and writes to Kafka, and the application writes to the database. The database is the source of truth, Kafka is the notification channel, and ZooKeeper is what Kafka and the CDC component both use for coordination, which the container list confirms by describing it as used by both the change data capture component and Kafka.
So the minimum viable deployment is four moving parts plus your own service, and the Docker Compose path is the way the README suggests for getting there.
The Docker Compose path, and the DOCKER_HOST_IP trap
The quick setup is described as the fastest way to get started, and it has one prerequisite that catches people out.
You must first set the environment variable DOCKER_HOST_IP to the IP address of the machine running Docker. If you are running Docker Machine on Mac or Windows, this would be the IP address of the VirtualBox VM. And the README is explicit that you cannot set DOCKER_HOST_IP to localhost, since that will not resolve to the correct IP address within a Docker container.
That is not a stylistic warning. Every connection string in the setup points at that address, so a localhost value produces containers that start and then fail to reach the database.
After the variable is set, you copy docker-compose-eventuate-local.yml into your project and launch the containers.
docker-compose -f docker-compose-eventuate-local.ymlThat creates four containers. Apache Zookeeper, used by both the change data capture component and Kafka. Apache Kafka, the message broker. MySQL, the database with the eventuate schema already defined. And cdcservice, the change data capture component.
The MySQL container arriving with the schema preloaded is a convenience that saves the first round of manual schema work, and it is also a reminder that the schema is owned by the project rather than by your application migrations.
If you already have a compose file, the README notes you can add the contents of this one to your project's docker-compose.yml, or run docker-compose with multiple -f arguments.
docker-compose -f docker-compose-eventuate-local.yml -f docker-compose.yml up -dThe two -f form is worth remembering, because it is what lets your application container sit in the same compose project as the Eventuate containers.
The properties your Spring Boot application needs
Once the containers are up, the application has to be told where everything is, and the README gives the Spring Boot properties and the matching environment variables.
The application properties are the datasource URL, which points at the MySQL container, the datasource username and password, the Kafka bootstrap servers, the ZooKeeper connection string, and the change data capture database credentials.
spring.datasource.url=jdbc:mysql://${DOCKER_HOST_IP}/eventuate
eventuateLocal.kafka.bootstrapServers=$DOCKER_HOST_IP:9092
eventuateLocal.zookeeper.connectionString=$DOCKER_HOST_IP:2181
eventuateLocal.cdc.dbUserName=root
eventuateLocal.cdc.dbPassword=rootpasswordThe environment variable form follows the Spring Boot relaxed binding rules, so the same settings become SPRING_DATASOURCE_URL, EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS, EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING and the CDC user and password variables.
export EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS=$DOCKER_HOST_IP:9092
export EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING=$DOCKER_HOST_IP:2181The README points at scripts/set-env.sh as a convenient way to set them, which is a shell script in the repository that does the export for you.
The containerised form is different in one important way, and it is the difference between the two code blocks above. Inside a compose network the hostnames are the service names, so the datasource URL becomes jdbc:mysql://mysql/eventuate style values, kafka:9092 and zookeeper:2181, and the container needs links to the mysql, kafka and zookeeper services to resolve them.
mycontainer:
links:
- mysql
- kafka
- zookeeper
environment:
EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS: kafka:9092
EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING: zookeeper:2181Forgetting to set DOCKER_HOST_IP is the common first failure, and forgetting that the in-container hostnames are service names is the common second one.
Gradle coordinates, and which artifacts to use instead
If you are building with Gradle, the README says to set a version property in gradle.properties.
eventuateLocalVersion=0.11.0.RELEASEAnd then to specify the Eventuate Local artifacts instead of the Eventuate HTTP and STOMP artifacts.
compile "io.eventuate.local.java:eventuate-local-java-jdbc:${eventuateLocalVersion}"
compile "io.eventuate.local.java:eventuate-local-java-embedded-cdc-autoconfigure:${eventuateLocalVersion}"The second coordinate is the one to look at carefully. It is the embedded change data capture auto configuration, which means the application container includes the CDC component and tails the database itself, rather than relying on the separate cdcservice container.
So there are two ways to run the change data capture, and the artifact choice tells the framework which one. The separate service means one tailer for the whole system, and the embedded variant means each application instance tails the database on its own, which is simpler to deploy but means several tailers reading the same log.
The repository also ships compose files for more than one database. Alongside docker-compose-eventuate-local.yml there are docker-compose-mssql.yml, docker-compose-mysql.yml and docker-compose-postgres.yml in the root, which suggests other database backends have been tried, even though the README says MySQL currently, and the CDC component is described as tailing the MySQL transaction log specifically.
The module list in the repository is much larger than the two artifacts above, with separate trees for Spring, Micronaut and Quarkus, and for common, crud, events, jdbc, autoconfigure and test variants of each, so the published surface is wide even though the documented starting point is one coordinate and one version.
The manual setup path is unfinished and the licence is custom
Two things in this repository deserve a flag before you rely on it.
The first is the section titled The not so quick version, which is the manual, non-Docker setup path. Its entire content in the README is a single line stating that it is not written yet.
That is not a criticism of the project so much as a warning about what the documentation covers. The Docker Compose path is documented in detail, the properties and the environment variables are documented, the Gradle coordinates are documented, and the manual path is not. If your plan involves running this outside containers, on infrastructure you configure yourself, you are working from the compose files and the source rather than from a guide.
The example applications are the next thing to look at. The README says the Eventuate example applications support both Eventuate and Eventuate Local, which means the same sample code runs against the hosted product and the local edition, and that is the best way to see what an application actually has to do differently.
The second flag is the licence. The repository is recorded as carrying a custom licence that GitHub cannot classify, with a pointer to the LICENSE.md file, and the README does not restate the terms anywhere. For a platform that will sit between your services and your database, that is a document to read before you commit, and it is the one piece of information this article cannot give you.
On the practical side, the project has an active cadence, with the last push on 2026-10-22 and a CircleCI configuration in the root, and the community channels listed are a Google group, a Slack workspace and a contact form, with paid support offered separately through the Eventuate site.
Editorial conclusion
Eventuate Local suits a team that wants the Eventuate programming model, with the same API as the hosted product, but running on infrastructure it controls, especially where the event store must live in a database you already back up and audit. It does not suit a team that wants a managed service, since the local edition means running MySQL, Kafka and ZooKeeper yourself, and it does not suit a project that has decided to avoid change data capture, since that is the mechanism the whole design rests on. Before you commit, read the CDC section carefully and confirm your MySQL version is one the change data capture component can tail, follow the Docker Compose path first because the manual path in the README is unfinished, and note that the licence is a custom one recorded in LICENSE.md rather than a standard grant, and that the last push to the repository was on 2026-10-22.
Frequently asked questions
What is Eventuate Local?
It is the on premise, open source version of Eventuate, a platform for developing transactional business applications that use the microservice architecture with an event-driven programming model based on event sourcing and CQRS. It has the same API as the SaaS version but uses a SQL database for events and Kafka for publish and subscribe.
How does Eventuate Local publish events to Kafka?
Through a change data capture component that tails the MySQL transaction log and publishes each event inserted into the EVENTS table to the Kafka topic for that event's aggregate. The CDC component runs either embedded within each application or as a separate service called cdcservice.
How do I start Eventuate Local with Docker Compose?
Set DOCKER_HOST_IP to the IP address of the Docker host, which cannot be localhost, copy docker-compose-eventuate-local.yml into your project, and run docker-compose -f docker-compose-eventuate-local.yml. That creates containers for Zookeeper, Kafka, MySQL with the eventuate schema, and cdcservice.
Which Gradle artifacts do I use for Eventuate Local?
Set eventuateLocalVersion in gradle.properties, for example to 0.11.0.RELEASE, and depend on io.eventuate.local.java:eventuate-local-java-jdbc and io.eventuate.local.java:eventuate-local-java-embedded-cdc-autoconfigure instead of the Eventuate HTTP and STOMP artifacts.
Does Eventuate Local support databases other than MySQL?
The README describes the event store as a SQL database, currently MySQL, and the change data capture component as tailing the MySQL transaction log, although the repository also ships compose files for mssql, mysql and postgres.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/eventuate-local-eventuate-local)Was this useful?