Why orchestrate the sync
A hand-written RabbitMQ to Meilisearch consumer means managing connections, acknowledgements, deserialization, batching, retries, restarts, and monitoring, a service to keep alive. Kestra collapses that into a trigger plus tasks, with acknowledgement, retries, and observability handled for you.Prerequisites
A running Kestra with three plugins (Meilisearch, AMQP, and the transform plugin for reshaping messages), plus a Meilisearch Cloud project and a RabbitMQ broker: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). Store the key as the MEILISEARCH_API_KEY Kestra secret, and keep broker credentials in secrets too.{ "id": "prod-1", "name": "Mechanical Keyboard", "stock": 42 }.
Understanding the shape: a batch consume-and-index flow
Before wiring up real-time, here’s a flow you can run on demand. It declares a queue, publishes a few test messages, consumes them, reshapes them, and indexes them:extract_payload. The Consume task writes a full AMQP envelope per message: data (the body), plus headers, contentType, messageId, timestamp, and so on. You don’t want all that in your search index, just the body, so a JSONata TransformItems with expression: data plucks the payload out of each record. (In this demo create_queue and publish set the stage. In production you’d delete them, since your services already produce to the queue.)
The real deal: real-time indexing
Kestra’s AMQPRealtimeTrigger holds a persistent consumer open and starts one execution per message the instant it’s delivered. A published event is searchable in Meilisearch within seconds, with no cron and no polling.
{{ trigger.data }}. The flow writes it to internal storage as an ION document and indexes it. Publish 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 right when a queue carries a changelog of your entities.
Handling deletes
Represent deletions as messages and act on them. Publish an explicit delete event, for example{ "id": "prod-1", "op": "delete" }, and 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.data does the routing:
["prod-1"]) to documents/delete-batch. 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 tasks.
create_queueandpublishare only there to generate test data. In production your services produce to the queue and Kestra only consumes. - Real-time versus batch. The
RealtimeTriggergives second-scale freshness, one execution per message. For very high volume, the batchConsumepattern with a largermaxRecordson a schedule is more efficient. Choose freshness or throughput. - Acknowledgement. By default the consumer acks messages as it processes them, so a restart resumes from the queue without reprocessing. Your incrementality comes for free from the broker.
- Retries. Add a
retryblock so a transient Meilisearch error re-attempts the message rather than dropping it. Pair with a dead-letter queue on the broker for poison messages.
Wrap-up
Kestra turns “keep Meilisearch in sync with a RabbitMQ queue” into a trigger and a couple of tasks. TheRealtimeTrigger gives you live indexing with acknowledgement handled for you. Add-or-replace semantics make the stream an idempotent upsert feed, and deletes are just another message type. Publish events as you already do, and Kestra keeps search live.