Why orchestrate the sync
A hand-written Kafka to Meilisearch consumer means managing offsets, deserialization, batching, back-pressure, retries, restarts, and monitoring: a whole service. Kestra collapses that into a trigger plus two tasks, with offset tracking, retries, and observability handled for you. You focus on the shape of the data, not the plumbing.Prerequisites
A running Kestra with three plugins (Meilisearch, Kafka, and the transform plugin for reshaping messages), plus a Meilisearch Cloud project and a Kafka broker. Meilisearch is managed. For a local broker, Redpanda is a lightweight, Kafka-API-compatible option:Get your Cloud credentials. In the Meilisearch Cloud dashboard, create a project and copy its Project URL (the
url in the flows below) and its Default Admin API Key (Settings, then API Keys), then store the key as the MEILISEARCH_API_KEY Kestra secret.Understanding the shape: a batch consume-and-index flow
Before wiring up real-time, it helps to see the pieces in a flow you can run on demand. This one produces a few test messages, consumes them back, reshapes them, and indexes them:extract_values. Kestra’s Consume task writes a full record envelope for each message: key, value, topic, partition, offset, timestamp, headers. You don’t want all that in your search index, just the payload. The JSONata TransformItems task with expression: value plucks the value field out of each record, leaving clean product documents for DocumentAdd. (In this demo the produce task stands in for your real upstream. In production you’d delete it.)
The real deal: real-time indexing
Polling in batches adds latency and offset bookkeeping. Kestra’s KafkaRealtimeTrigger does better: it holds a persistent consumer open and starts one execution per message the instant it arrives. A produced event is searchable in Meilisearch within a couple of seconds, with no cron and no polling loop.
{{ trigger.value }}. The flow writes it to internal storage as an ION document and indexes it. That’s the entire live pipeline: produce a message to live-products, and it shows up in search seconds later.
Because DocumentAdd is add-or-replace, this is automatically an upsert stream. Publish an updated event for prod-1 and it overwrites the existing document by primary key, exactly the semantics you want when a topic carries a changelog of your entities.
Handling deletes
Represent deletions as events, then act on them. The idiomatic Kafka approach is a tombstone or an explicit delete event, for example{ "id": "prod-1", "op": "delete" }. Branch on it inside the per-message execution: route delete events to Meilisearch’s documents/delete-batch endpoint and everything else to DocumentAdd. An If task reading trigger.value does the routing:
["prod-1"]) to documents/delete-batch; the upsert branch is the same write-then-index pair from the real-time flow above. For the database-side soft-delete pattern (query recently-deleted rows, then delete-batch), see Connect PostgreSQL to Meilisearch with Kestra.
Going to production
- Drop the producer. The
Producetask in the batch example is only there to generate test data. In production your services (or a Debezium connector) produce to the topic. Kestra only consumes. - This is your CDC sink. Point Debezium at your PostgreSQL/MySQL/MongoDB WAL or oplog, stream changes into Kafka, and this flow becomes a true change-data-capture pipeline into Meilisearch, every row-level change captured in order, in real time.
- Throughput. For very high message rates, the batch
Consumepattern with a largermaxRecordsand a schedule can be more efficient than one execution per message. Choose real-time for freshness, batch for volume. - Consumer groups mean incrementality. A stable
groupIdmeans Kafka tracks the committed offset, so a restarted flow resumes exactly where it left off, with no missed or double-processed messages and no watermark to manage yourself. - Retries. Add a
retryblock so a transient Meilisearch error re-attempts the single message rather than dropping it.
Wrap-up
Kestra turns “keep Meilisearch in sync with a Kafka topic” into a trigger and two tasks. TheRealtimeTrigger gives you second-scale freshness with offset tracking handled for you. Add-or-replace semantics make the stream an idempotent upsert feed. Paired with Debezium, the same flow is a full CDC sink. Publish events as you already do, and Kestra keeps search live.