Should I put a Kafka/Redis buffer between Fluent Bit and OpenSearch when shipping logs?
Question
We aggregate logs from our Go and Laravel services into a central OpenSearch cluster. The flow: containers write to stdout, Fluent Bit on the hosts picks them up and ships straight to the OpenSearch API. But above ~40,000 log lines per second, OpenSearch indexing becomes the bottleneck; Fluent Bit can't keep up, backpressure builds, and the app containers' I/O blocks. Should I put a Redis list or Kafka in between as a buffer? How do I avoid data loss (at-least-once) while keeping disk/memory from ballooning on the hosts?
Answer
Short answer: yes, a buffer is the right move — but the choice between a Redis list and Kafka comes down to the guarantee level you’re willing to accept.
Short answer
What you’re hitting is classic backpressure: once OpenSearch’s indexing rate drops below your production rate, the chain clogs backwards until the app container’s stdout blocks. The fix is a durable layer that decouples the producer (your app) from the consumer (OpenSearch).
Why
-
A Redis list is cheap but risky. In-memory;
RPUSH/LPOPis simple, but with AOF off you can lose the latest writes when Redis stops uncleanly and a growing list eats Redis’s RAM. Fine for logs you don’t mind losing, not for financial/audit logs. -
40k/s is Kafka’s home turf. Disk-based, with retention, replayable, horizontal consumption via consumer groups. It gives you
at-least-oncenaturally. The cost is operational — you have to stand up and feed a cluster. If the volume is permanent, it’s the right tool.
What to do
-
Turn on Fluent Bit’s own disk buffer first. With
storage.type filesystemon the input andstorage.total_limit_sizeon the output, Fluent Bit spools to disk instead of RAM when OpenSearch slows down, so no pressure reaches the app. Once the output hits that limit, the oldest chunk is discarded, so size it for the longest slowdown you expect. Measure this before adding an external queue — it’s often all you need. -
Move to Kafka only if the volume is permanent. Decide by asking whether 40k/s is a temporary peak or the new floor; disk buffering covers temporary peaks.
-
Make at-least-once idempotent at the consumer. When writing to OpenSearch, give each document a deterministic
_id(e.g. a hash of source+offset). A message delivered twice overwrites the same_idinstead of creating a duplicate. For the worker and retry mechanics on the queue side, see the Laravel queue and Supervisor post.
Bottom line: start with Fluent Bit filesystem buffering plus bulk tuning and ISM (rollover) on the OpenSearch side. If that’s not enough, move to Kafka — keep Redis for logs you can afford to lose. And the golden rule: never let the app container block while logging. Logging should be fire-and-forget; don’t carry critical data (payments, audit) over the log pipeline — run that on a separate, guaranteed path.
Related Reading
Comments
Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.