Everything for Filebeat in one place — pick a section below. 13 reviewed items across 4 content types.
Quick Answer
Filebeat is deliberately built to move log data, not transform it — it's a lightweight, single-binary Go agent that tails files and forwards events, leaving heavy parsing (grok, complex conditionals, enrichment) to Logstash or Elasticsearch Ingest pipelines downstream. This separation lets Filebeat run cheaply on every host or container as a sidecar/daemonset with a small, predictable resource footprint. Logstash and Fluentd, by contrast, are built to also process and route events, which costs more CPU/memory but buys richer in-flight transformation.
Detailed Answer
Think of the difference between a courier who picks packages up from your doorstep and drives them to a regional sorting depot, versus the depot itself. The courier's entire job is to show up reliably, pick up whatever is there, and get it to the depot intact and on time — they don't open boxes, they don't repackage contents, and they don't decide which truck a package should ultimately go on. The depot does that heavier work: scanning barcodes, sorting by destination, relabeling, consolidating shipments. You want thousands of couriers running cheaply and reliably across a city, but you only need a handful of well-resourced depots doing the complex sorting.
Filebeat is the courier. It's written in Go and compiles to a single static binary with a small, predictable memory footprint, specifically so it can run on every host, VM, or container — including resource-constrained edge nodes — without becoming a resource problem itself. It deliberately excludes a grok engine, complex conditional routing, and heavyweight transformation, because embedding that machinery into an agent that runs on every node watching every file would multiply your fleet-wide CPU and memory cost by however many nodes you operate. Logstash runs on the JVM and is built to do exactly that heavy lifting — grok parsing, mutate, ruby filters — which is fine because you run far fewer Logstash instances than Filebeat instances. Fluentd sits architecturally in between, with a plugin-based routing model and moderate processing capability.
This split is visible in Filebeat's harvester/input model. An input configuration block defines what to watch — a path glob for payments-api's log directory, a container autodiscover rule, a syslog listener — and for every matching file Filebeat starts a harvester, a lightweight per-file reader. Events pass through only libbeat's built-in processors: add_fields, drop_fields, dissect, decode_json_fields — all non-Turing-complete, bounded operations, not a full parsing language. The event is then handed to the internal queue and pushed to the configured output. There's no step anywhere in that pipeline where Filebeat evaluates an arbitrary regex-driven grok pattern against every line at scan time; that work happens later, either in Logstash's pipeline or an Elasticsearch ingest pipeline, applied centrally rather than per-node.
In production, this means you should expect Filebeat's CPU and memory usage to stay flat and low regardless of log volume nuance — if you see Filebeat's CPU spike, it's almost always a misused processor (commonly a script processor doing per-event logic it wasn't meant for) or a misconfigured multiline pattern, not the shipping itself. Teams running Filebeat across checkout-worker and fraud-detection-svc fleets typically budget well under 100MB RSS per instance and treat any deviation as a config smell, while Logstash or the ingest-pipeline layer is scaled and monitored separately as the actual processing tier.
The non-obvious trap is engineers trying to make Filebeat do Logstash's job — stacking dissect, decode_json_fields, and script processors to replicate complex conditional parsing — and then wondering why Filebeat's memory grows and file-descriptor churn increases. The fix isn't more Filebeat tuning; it's recognizing you've asked a courier to run the sorting depot, and moving that logic downstream. A related trap: people assume Filebeat ships 'whole files' the way you'd hand off a sealed package, but it actually reads line by line, so a single log line exceeding the configured line-size limit gets truncated rather than deferred — very different behavior from what the shipping analogy implies at first glance.
Code Example
filebeat.inputs:
- type: log
paths:
- /var/log/checkout-worker/*.log # shipper only watches raw text, no parsing here
processors:
- add_fields: # lightweight libbeat enrichment, not a parser
target: ""
fields:
service: checkout-worker
- drop_fields:
fields: ["agent.ephemeral_id"] # trim noise before shipping, still no regex/grok
output.logstash:
hosts: ["logstash.internal:5044"] # heavy parsing (grok, mutate) happens downstream
loadbalance: true # spread load across multiple Logstash nodes
# validate syntax and connectivity before touching production
filebeat test config -c filebeat.yml
filebeat test output -c filebeat.ymlInterview Tip
A junior engineer typically answers that 'Filebeat is lightweight and Logstash is heavier,' but for a senior/architect role, the interviewer is actually looking for you to explain why that resource asymmetry is an intentional architectural boundary, not just a performance detail — Filebeat runs at fleet scale (one per node) so it must stay cheap, while processing tools run at a much smaller scale so they can afford to be expensive per instance. Strong candidates also flag where the boundary gets blurred in practice, like teams overloading Filebeat processors with logic that belongs in an ingest pipeline, and can describe the operational symptom (rising per-node CPU/memory) that reveals the boundary was crossed.
◈ Architecture Diagram
┌─────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐
│ App Logs│ → │ Filebeat │ → │ Logstash │ → │Elasticsrch│
└─────────┘ │ (shipper) │ │(processor)│ └───────────┘
└───────────┘ └───────────┘
no grok here grok/mutate here💬 Comments
Quick Answer
Check the libbeat monitoring API: if output.events.dropped is zero but the gap between published and acked events is growing, Filebeat is applying backpressure — pausing harvesters rather than losing data — and the fix is to scale the queue or the output's ingest capacity. If dropped events or output write errors are nonzero, or a rotated file was deleted before its harvester finished reading it, that's genuine, permanent loss, and the fix is tuning logrotate/close_inactive settings and queue sizing so harvesters can always finish before their files disappear.
Detailed Answer
Imagine a metered highway on-ramp during rush hour. Cars queue up at the ramp meter when the highway ahead is congested — they're not gone, they're just later than usual, and eventually all of them get through once traffic clears. That's a normal, if annoying, backup. Now imagine the on-ramp has a hard capacity and no shoulder to wait on: once it's full, incoming cars are turned away entirely and never make the trip. From a distance, both situations look identical — traffic isn't coming through in real time — but one is a temporary delay and the other is permanent loss, and you cannot tell them apart just by watching the highway; you need to check specific gauges at the ramp itself.
Filebeat's internal queue is designed to behave like the metering ramp, not the capacity-capped one — by default it holds a bounded number of events in memory, and when that queue fills up, harvesters pause reading new lines from disk rather than discarding events. This is a deliberate at-least-once design choice: back-pressure is supposed to propagate all the way upstream to the file read itself, so overload manifests as delay, never as silent data loss. Real loss happens through a different mechanism entirely — most commonly when a log file gets rotated and deleted by logrotate faster than Filebeat's harvester can finish reading it, which is a file-lifecycle problem, not a queue problem.
To tell these apart, pull metrics from Filebeat's monitoring HTTP endpoint. A growing gap between libbeat.pipeline.events.published and libbeat.output.events.acked, with libbeat.output.events.dropped sitting at zero, means events are queued and delayed — classic backpressure, usually because the output (Elasticsearch or Logstash) is issuing bulk rejections or 429s faster than Filebeat can retry. Any nonzero value in dropped or in output write-error counters means events were actually discarded at the transport layer. Separately, compare registry offsets against the last known size of files that were rotated away: if a file was deleted while its recorded offset was still behind its final size, those unread bytes are gone permanently, with nothing in the pipeline metrics to flag it unless you're watching harvester-close and file-not-found log lines.
During checkout-worker's 10x spike, expect harvester count to climb as multiple rotated generations stay open simultaneously, the event queue to sit at its configured maximum, and the acked rate to flatline while Elasticsearch returns bulk rejections. The backpressure fix is capacity: raise queue.mem.events, tune bulk_max_size and worker count on the output, or scale the ingest tier itself. The permanent-loss fix is lifecycle coordination: make sure logrotate retains enough generations, and for long enough, that close_inactive and normal harvester read speed can always finish before a rotated file is actually deleted — and alert specifically on nonzero dropped/error metrics rather than on 'no new events,' which is ambiguous between healthy silence and a broken pipeline.
The gotcha senior engineers get bitten by: switching to Filebeat's disk-backed queue (queue.disk) to absorb bigger bursts without upstream blocking looks like it solves the backpressure problem, and for typical spikes it does — but the disk queue itself has a configurable size cap, and if nobody sizes or alerts on that, you've just moved the failure further downstream and made it quieter. Instead of harvesters visibly pausing, the host silently fills its disk with queued-but-unsent events until either the queue hits its cap (and now you're dropping) or the disk itself fills and Filebeat crash-loops — which reintroduces the exact 'file rotated out from under an unfinished harvester' loss scenario you were trying to avoid in the first place.
Code Example
queue.mem: events: 4096 # bounded in-memory queue; harvesters pause, don't drop, when full flush.min_events: 512 # batch size before pushing to the output flush.timeout: 1s # max wait before flushing a partial batch http.enabled: true # exposes internal metrics for diagnosis http.port: 5066 output.elasticsearch: hosts: ["es-ingest.internal:9200"] bulk_max_size: 200 # smaller bulk size reduces 429 rejections during a spike worker: 4 # parallel bulk senders to keep up with checkout-worker's volume # pull live counters to distinguish delay from real loss curl -s localhost:5066/stats | jq '.libbeat.pipeline.events, .libbeat.output.events'
Interview Tip
A junior engineer typically answers 'check if Filebeat is running and restart it,' but for a senior/architect role, the interviewer is actually looking for you to reason from specific metrics — dropped events versus published/acked gap — rather than guessing from symptoms like gaps in Kibana. They want to see you separate two genuinely different failure classes (transient backpressure versus permanent loss from file lifecycle mismatches) and propose different fixes for each, instead of one generic 'increase resources' answer. Bonus depth: mentioning that the disk-backed queue changes the failure mode rather than eliminating it shows you understand these mitigations shift risk rather than removing it.
◈ Architecture Diagram
┌──────────┐ ┌──────────┐ ┌────────┐ ┌────────┐
│Harvester │ → │Queue(mem)│ → │ Output │ → │Elastic │
└──────────┘ └──────────┘ └────────┘ └────────┘
│ │
full → pause dropped=✗ (loss)
harvester gap growing = delay💬 Comments
Quick Answer
A small set of metrics from Filebeat's monitoring endpoint predict real trouble: nonzero output.events.dropped (page immediately — active data loss), a sustained and growing gap between events published and acked (page — backpressure not draining), and harvester.open_files approaching the OS file-descriptor limit (page — imminent crash). Slower trends like gradual memory growth or occasional rotation-correlated acked-lag spikes are ticket-worthy, not page-worthy, because they need investigation but aren't actively losing data right now.
Detailed Answer
A nurse watching a patient overnight doesn't run a full lab panel every five minutes — she watches a small set of vitals: heart rate, blood oxygen, blood pressure. A sudden drop in blood oxygen gets a doctor paged immediately, because it predicts a crisis in progress. A slightly elevated resting heart rate over several days gets noted for the morning rounds and a follow-up test, not a 3am page. The skill isn't collecting more data, it's knowing which few signals, trending in which direction, actually precede a crisis — and reserving the alarm bell for those, because paging on every fluctuation trains everyone to ignore the alarm.
Filebeat exposes dozens of counters through its monitoring HTTP endpoint (enabled with http.enabled: true, scraped locally or shipped via Metricbeat's filebeat module into Stack Monitoring), and most of them are useful for a post-incident ticket, not for waking someone up. The reason only a handful predict outages is that Filebeat's failure modes are narrow and specific — resource exhaustion (file descriptors, memory), pipeline stall (queue not draining), and transport failure (output rejecting or dropping events) — and each has one or two counters that move meaningfully before user-visible symptoms like missing logs in Kibana show up.
The metrics worth paging on: libbeat.output.events.dropped and output write-error counts — any nonzero value means real, current data loss, not a warning sign of future loss. The gap between libbeat.pipeline.events.published and libbeat.output.events.acked, trended over 10-15 minutes rather than as an instantaneous snapshot (single spikes during normal log rotation are expected and self-correct) — a gap that keeps growing without draining means backpressure that isn't resolving on its own. filebeat.harvester.open_files climbing toward the process's file-descriptor ulimit — Filebeat will start failing to open new files and effectively stop ingesting once it hits that ceiling. Ticket-worthy signals include beat.memstats.memory_alloc creeping upward over days (possible leak, investigate on your schedule) and occasional acked-lag blips that correlate with known deploy or rotation windows.
Building this into a real on-call setup means a dashboard per fleet (payments-api, checkout-worker, fraud-detection-svc) with the paging thresholds wired to dropped-events-greater-than-zero, sustained published/acked gap beyond a chosen window, and open_files beyond roughly 80% of the configured ulimit — each backed by a runbook, because a page without a runbook just creates panic. The ticket-tier thresholds get reviewed weekly rather than fired reactively, since their value is catching slow degradation (memory creep, a harvester count that never comes back down after a rotation storm) before it becomes page-worthy.
The gotcha many teams fall into is alerting solely on 'no new events shipped in N minutes' as their entire Filebeat health signal. That metric is fundamentally ambiguous: it fires identically whether payments-api simply had a quiet night with no traffic, or Filebeat itself is completely wedged and shipping nothing from a service that's actually generating errors right now. The fix experienced teams adopt is a synthetic heartbeat — a canary log line written on a fixed interval purely so its absence downstream is an unambiguous 'the Filebeat pipeline is broken end-to-end' signal, decoupled entirely from whatever real application traffic happens to be doing.
Code Example
http.enabled: true # required to expose the monitoring endpoint locally http.host: 127.0.0.1 http.port: 5066 # page-worthy: any dropped events means real data loss right now curl -s localhost:5066/stats | jq '.libbeat.output.events.dropped' # page-worthy: growing gap = backpressure building, not draining curl -s localhost:5066/stats | jq '.libbeat.pipeline.events.published, .libbeat.output.events.acked' # ticket-worthy: slow trend, check once per day not every minute curl -s localhost:5066/stats | jq '.beat.memstats.memory_alloc'
Interview Tip
A junior engineer typically answers 'alert if Filebeat stops sending logs,' but for a senior/architect role, the interviewer is actually looking for you to name specific counters (dropped events, the published/acked gap, open file handles) and explain why each one predicts a distinct failure mode rather than treating 'no events' as one generic health check. They also want to hear you distinguish paging thresholds from ticket thresholds deliberately, because over-paging on noisy metrics trains responders to ignore alerts, and under-paging on real ones delays incident response. Mentioning a synthetic heartbeat to disambiguate 'quiet service' from 'broken pipeline' is the kind of detail that separates rote metric-naming from real operational maturity.
◈ Architecture Diagram
┌───────────────┐ ✗ page now │ dropped > 0 │ ──────▶ (active loss) └───────────────┘ ┌───────────────┐ ✗ page (10m+) │ pub-ack gap │ ──────▶ (backpressure) └───────────────┘ ┌───────────────┐ ● ticket │ memory creep │ ──────▶ (investigate) └───────────────┘
💬 Comments
Quick Answer
Treat Filebeat config changes like any risky fleet-wide deploy: validate with filebeat test config/test output first, then roll out in canary rings (1% to 10% to 50% to 100%) through your config-management tool, watching harvester errors and dropped/acked metrics per ring before promoting. For multiline pattern changes specifically, test the regex offline against real historical log samples and bound multiline.max_lines/timeout, because a bad pattern doesn't crash Filebeat — it silently corrupts event boundaries at scale, which is worse than a visible failure.
Detailed Answer
Cities don't retime every traffic light simultaneously when rolling out a new signal-timing algorithm. They pilot it at a handful of intersections first, watch how traffic actually flows through them for a period, and only then expand to more intersections, because a bad citywide timing change causes gridlock everywhere at the exact same moment, during actual rush hour, with no way to quickly route around it. A city engineer who changed every light in town at once because the new algorithm 'tested fine on paper' would be making the same mistake as pushing an untested Filebeat config to every host in the fleet simultaneously.
Filebeat config changes are risky in a specific, non-obvious way: a bad multiline pattern (the regex-driven rule that decides whether a line continues the previous log entry, used to stitch multi-line stack traces back into one event) doesn't crash the process — it silently merges or splits events incorrectly, corrupting event boundaries across every host that has the new config, before anyone notices in Kibana. A bad new input path with wrong permissions similarly fails quietly, with harvesters unable to open files rather than throwing an obvious error. Because Filebeat can hot-reload modular input configs, a fleet-wide simultaneous push can also become a fleet-wide simultaneous failure mode within seconds, not the gradual failure you'd get from a slower deployment method.
The safe mechanics: use centralized config management (Ansible, Salt, or Fleet-managed Elastic Agent policies) with canary rings rather than a single fleet-wide push — 1%, then 10%, then 50%, then 100%, with a pause and metrics check between each ring. Run filebeat test config and filebeat test output before any push to catch syntax errors and connectivity problems without touching a live pipeline. For multiline changes, test the new regex against a sample of real historical log lines from order-fulfillment or fraud-detection-svc offline, not just a handful of synthetic examples. Ship input configs as drop-in files under an inputs.d directory with reload.enabled: true so Filebeat can hot-reload them — but verify in your specific version whether hot reload actually re-evaluates multiline logic on already-open harvesters, or only affects newly discovered files, since assuming the wrong behavior is a common source of surprises.
At fleet scale, wire the same monitoring-API metrics used for day-to-day health — harvester errors, config-reload failure counts (Filebeat logs a distinct error when a pushed config is invalid), and output.events.dropped — into the canary pipeline itself, segmented per ring, so a regression introduced in ring 1 is caught and blocks promotion before it ever reaches ring 4. Automate the rollback trigger: if error rate or dropped-events rate in the active canary ring exceeds baseline by a defined factor, halt promotion and revert that ring's config automatically rather than waiting for a human to notice a dashboard.
The failure senior engineers have genuinely been burned by: a multiline pattern change passes validation, looks correct in canary hosts for hours, then a real production incident on fraud-detection-svc produces an unusually deep, pathological stack trace that hits a regex edge case — catastrophic backtracking or a pattern that misbehaves specifically on deeply nested frames — spiking Filebeat's CPU right as the incident needs logs the most, turning a service incident into an observability blackout simultaneously. The mitigation is bounding multiline.max_lines and multiline.timeout aggressively so a pathological match can't run away, and load-testing the regex against deliberately ugly, unusual inputs before any fleet rollout, not just the typical stack traces you have on hand.
Code Example
# inputs.d/checkout-worker.yml - dropped into an inputs.d directory for hot reload
- type: log
paths:
- /var/log/checkout-worker/*.log
multiline.pattern: '^\d{4}-\d{2}-\d{2}' # new pattern under test, matches ISO date prefix
multiline.negate: true # lines NOT matching the date are continuations
multiline.match: after
multiline.max_lines: 200 # bound worst-case stack trace size
multiline.timeout: 5s # bound worst-case wait before flushing
# validate syntax and connectivity before touching production
filebeat test config -c /etc/filebeat/filebeat.yml
# canary: push to 1% of fleet first via your config-management tool
ansible-playbook deploy-filebeat.yml --limit 'canary_ring_1'Interview Tip
A junior engineer typically answers 'push the config with Ansible to all hosts,' but for a senior/architect role, the interviewer is actually looking for a canary-based rollout strategy with automated rollback triggers tied to specific Filebeat metrics, not just a deployment mechanism. They want to hear that you understand multiline pattern bugs are uniquely dangerous because they fail silently — corrupting event boundaries rather than crashing — which is why testing the regex against real historical data matters more than for most config changes. The strongest answers also mention that a bad pattern can become dangerous precisely during a real incident, when unusual stack traces first exercise an edge case, compounding one outage with an observability failure.
◈ Architecture Diagram
┌──────┐ ┌──────┐ ┌──────┐ ┌───────┐ │Ring 1│ → │Ring 2│ → │Ring 3│ → │Ring 4 │ │ 1% │ │ 10% │ │ 50% │ │ 100% │ └──────┘ └──────┘ └──────┘ └───────┘ │ │ │ │ ✓ ✓ ✓ ✓ check check check done metrics metrics metrics
💬 Comments
Quick Answer
Filebeat maintains a registry file that records the filesystem inode, device ID, and byte offset for every file it has harvested, using that stable identity — not the filename — to resume exactly where it left off after rotation, restarts, or crashes. It only advances an offset after the output acknowledges receipt of the corresponding events, which gives at-least-once delivery: Filebeat may occasionally re-send a line, but it will not silently skip one. Data loss only happens when a file is deleted or truncated faster than its harvester can catch up.
Detailed Answer
Picture a librarian who is simultaneously reading through hundreds of books, switching between them all day. Instead of trying to remember 'I was on page 42 of the blue book on the third shelf,' she keeps a notebook with one line per book, identified by that book's unique barcode rather than its title — because titles get reprinted, covers get replaced, and books get reshelved constantly. Each line records the exact page she reached. If she loses her place and picks the notebook back up, she can find the barcode and resume from that exact page, even if the book's cover looks completely different than it did yesterday. What she cannot survive is someone tearing pages out of a book faster than she can read them — those pages are simply gone before she gets to them.
In Filebeat, that notebook is the registry file, and the barcode is the combination of filesystem inode and device number rather than the file path. This design exists because production log files are rarely stable: logrotate renames access.log to access.log.1, Docker's json-log driver rotates container logs, and applications like payments-api or order-fulfillment truncate and recreate files on deploy. A filename-based bookmark would break constantly under rotation, either re-reading whole files from scratch or losing the tail of a renamed file mid-read. By keying off inode and device, Filebeat treats a renamed file as the same file it was already tracking, and can keep reading from the correct offset uninterrupted.
Internally, each configured input scans its paths and, for every matching file, spins up a harvester — a dedicated goroutine that opens the file and reads new lines as they're appended. Each line becomes an event that flows through libbeat's internal queue toward the configured output (Elasticsearch, Logstash, or Kafka). Crucially, Filebeat does not update the registry offset the moment it reads a line; it waits for an ACK (acknowledgment) from the output confirming the event was durably received. Only then does the registrar component persist the new offset to disk. This ACK-gated commit is what makes the delivery guarantee at-least-once: if Filebeat crashes between reading and receiving the ACK, it re-reads and re-sends those lines on restart rather than assuming they arrived.
At scale — say hundreds of hosts each running Filebeat against payments-api, checkout-worker, and fraud-detection-svc logs — the registry can accumulate thousands of entries, and registry write frequency becomes a real I/O concern, which is why modern Filebeat writes registry state incrementally to a directory rather than rewriting one giant file. Operators should monitor open harvester counts, the gap between events read and events acked, and registry flush latency. A registry that stops advancing while harvesters stay open is a strong signal of a stalled output or a downstream outage, not necessarily lost data — yet.
The gotcha experienced engineers eventually hit is truncation and inode reuse. If a log file is truncated in place (some naive rotation scripts do a hard truncate instead of proper copytruncate coordination) rather than renamed, Filebeat detects the file is now smaller than its recorded offset and resets to zero — but depending on timing, that can produce duplicate reads of whatever was written between truncation and detection. Worse, on filesystems or container runtimes that aggressively reuse inode numbers (fresh volumes after a pod restart, some NFS mounts), a brand-new file can inherit the same inode as a previously fully-processed and pruned file, and Filebeat will assume it's already been shipped — silently skipping real, new log data.
Code Example
filebeat.inputs:
- type: log
paths:
- /var/log/payments-api/*.log # each matched file gets its own harvester
close_inactive: 5m # release the file handle after 5m of no new lines
close_removed: true # stop harvesting immediately if the file is deleted
clean_removed: true # drop the registry entry once the file is gone
ignore_older: 24h # never start harvesting files older than 24h
filebeat.registry.path: /var/lib/filebeat/registry # where inode+offset state is persisted
filebeat.registry.flush: 1s # how often registry writes are fsynced to disk
# inspect the live registry state for a given input
cat /var/lib/filebeat/registry/filebeat/log.json | jq '.[] | {source, offset, FileStateOS}'
# FileStateOS.inode + FileStateOS.device uniquely identify the file, not its pathInterview Tip
A junior engineer typically answers that Filebeat 'remembers where it left off in a file,' but for a senior/architect role, the interviewer is actually looking for whether you understand that Filebeat's guarantee is at-least-once, not exactly-once, and why that tradeoff was chosen deliberately — durability over deduplication complexity. They want to hear you reason about the ACK-gated registry commit as the mechanism that prevents silent data loss, and they want you to volunteer the failure modes that break the guarantee anyway: in-place truncation, inode reuse after aggressive pruning, and rotation policies that outrun close_inactive settings. Candidates who only describe the happy path miss the point of the question; the depth is in naming what still goes wrong.
◈ Architecture Diagram
┌────────┐ ┌─────────┐ ┌───────┐ ┌────────┐
│Log File│→ │Harvester│→ │ Queue │→ │ Output │
└────────┘ └─────────┘ └───────┘ └────────┘
↑ │
└────────── ACK confirms ────────────┘💬 Comments
Context
A cluster where each team had solved logging independently: eleven sidecar variants (fluentd, fluent-bit, custom shippers) burning ~15% of cluster resources, each with its own parsing bugs and no shared metadata standard.
Problem
Sidecars doubled pod resource footprints, log metadata was inconsistent (some teams had namespace/pod labels, some didn't), multiline stack traces were mangled differently per shipper, and platform had no single point to fix anything.
Solution
Replaced all sidecars with one Filebeat DaemonSet using autodiscover with hints: Filebeat watches the kubelet API, discovers container logs, and enriches every event with pod/namespace/labels metadata automatically; teams customize parsing via pod annotations (co.elastic.logs/multiline.pattern, .../json.keys_under_root) instead of running their own shipper. Platform owns the DaemonSet config (resource limits, registry persistence on hostPath, backpressure settings); teams own their annotations. Rollout was namespace-by-namespace with dual-shipping comparison, then sidecar eviction enforced by admission policy.
Commands
filebeat.autodiscover: {providers: [{type: kubernetes, hints.enabled: true}]}pod annotation: co.elastic.logs/multiline.pattern: '^\d{4}-' co.elastic.logs/multiline.negate: 'true'kubectl top pods -A | grep -c sidecar # before: 400+, after: 0
Outcome
Cluster resource usage dropped ~12% (sidecars gone); every log line now carries uniform Kubernetes metadata (incident correlation across services finally works); multiline handling is consistent; and the next parsing fix shipped once, in one DaemonSet, for everyone.
Lessons Learned
The annotation-based hints model was the adoption key — teams kept control of their parsing without owning infrastructure. Registry persistence via hostPath matters on node restarts; the first draft lost file positions and re-shipped duplicates on every node reboot.
💬 Comments
Context
A logging architecture where Filebeat shipped directly to Elasticsearch — meaning every ES degradation immediately became a log-collection incident: harvesters throttled, files rotated away unread, and events vanished.
Problem
A 4-hour Elasticsearch outage (the flood-stage incident) had cost ~2 hours of logs from high-churn services whose files rotated past retention while Filebeat waited — the exact logs needed for the postmortem of the outage itself.
Solution
Inserted Kafka as the durable buffer: Filebeat outputs to a Kafka topic (partitioned by service, 24h retention sized for a full day of peak volume), Logstash consumes from Kafka into Elasticsearch. Filebeat's job shrank to 'tail and ship to the always-available broker' — its registry-based resume plus Kafka's retention means downstream outages cost latency, not data. Filebeat-side tuning completed the design: close_timeout bounded harvester lifetimes on rotating files, and queue settings sized for broker blips rather than multi-hour buffering.
Commands
filebeat: output.kafka: {hosts: [...], topic: 'logs-%{[service]}', required_acks: 1, compression: lz4}kafka: retention.ms=86400000 per logs topics; capacity = peak_rate x 24h x 1.3
monitor: kafka consumer lag (logstash group) + filebeat registry age
Outcome
The next Elasticsearch incident (90 minutes, unrelated cause) cost zero log loss — Kafka absorbed the backlog and Logstash drained it in 40 minutes after recovery; postmortem timelines are now complete by construction. Log delivery latency in steady state rose by ~2 seconds, accepted as the price.
Lessons Learned
The buffer belongs between the shipper and the fragile store — Filebeat's own spooling can't survive file rotation on high-churn sources no matter how it's tuned. Consumer-lag monitoring became the single most useful logging-pipeline health metric.
💬 Comments
Symptom
After restarting Filebeat (deploy, host reboot, crash recovery), the destination (Elasticsearch/Logstash/Kafka) receives a large burst of duplicate log events for files that had already been fully shipped before the restart.
Error Message
none single error — observed as a spike in ingested event volume with duplicate content/timestamps immediately following a Filebeat restart
Root Cause
Filebeat tracks read position per file in a local registry keyed by device and inode; multiple documented issues (elastic/beats#31924, #24208, and related) show this state can be lost or invalidated across restarts in specific conditions — a volume remount changing inode numbers (common in containerized/Kubernetes environments), registry file corruption, or the registry's configured cleanup interval expiring an entry for a still-relevant file — causing Filebeat to treat already-shipped content as new and re-harvest it from the beginning.
Diagnosis Steps
Solution
Identify why the registry lost track of the affected files (inode change from remount, registry corruption, premature cleanup) and, if downstream deduplication isn't already in place, add it (a fingerprint/hash-based dedup at the output/ingest stage) as a safety net; for the immediate duplicate flood, downstream consumers should be able to tolerate or filter the burst rather than treating it as new incident data.
Commands
filebeat export config
cat data/registry/filebeat/log.json | jq . | head -50
curl -s localhost:5066/stats | jq .filebeat
Prevention
In containerized environments, mount log volumes in a way that preserves stable inode/device identifiers across restarts where possible, and monitor registry file size/health as a standing signal. Add downstream deduplication (based on a stable per-event fingerprint, not just timestamp) as a defense-in-depth measure regardless of how well the registry is tuned, since this class of issue recurs across Filebeat versions.
💬 Comments
Symptom
A specific high-volume log file shows roughly double the expected event count in the destination, and Filebeat's internal metrics show more than one harvester active against the same file path at nearly the same timestamp.
Error Message
filebeat log: 'Harvester started for file' logged twice for the same file path within a few seconds
Root Cause
A documented Filebeat issue (elastic/beats#39567) shows multiple harvesters being created for the same log file within a span of seconds — typically triggered by rapid file rotation/truncation combined with Filebeat's file-scanning interval, where a new harvester starts for what looks like a 'new' file (due to rotation) before the old harvester has been properly closed out, resulting in both reading overlapping content simultaneously.
Diagnosis Steps
Solution
Confirm the duplicate-harvester pattern via Filebeat's internal metrics, then reduce scan_frequency or tune close_* harvester settings (close_inactive, close_renamed, close_removed) to match the actual log rotation behavior of the affected file, so a rotation is handled as a clean handoff rather than racing two harvesters; check whether the installed Filebeat version has a fix for the specific upstream issue.
Commands
curl -s localhost:5066/stats | jq '.filebeat.harvester'
filebeat -e -d 'harvester' 2>&1 | grep '<file_path>'
grep -c 'Harvester started' filebeat.log
Prevention
Align Filebeat harvester close settings explicitly with the actual log rotation tool/interval in use (e.g. logrotate frequency) rather than leaving defaults that assume a different rotation pattern. Monitor harvester count per file path as an anomaly signal — more than one harvester on the same path is never expected in steady state.
💬 Comments
Symptom
Nodes alert on disk full, but du can't find the space: the filesystem reports 40GB more used than any file listing accounts for. Application logs rotate normally; deleting old logs frees nothing. One node hits 100% and evicts pods.
Error Message
df: /var 98% used. du -sh /var/log: 6.2G. lsof reveals: filebeat holding dozens of '(deleted)' file descriptors on rotated log files, some weeks old, totaling the missing 40GB.
Root Cause
Filebeat harvesters keep files open until fully shipped and close conditions trigger — with the downstream output intermittently slow (an overloaded Logstash), harvesters never caught up on chatty files, and the default close behavior (close on EOF/inactivity, but the files never went inactive before rotation) meant Filebeat held file descriptors on log files that logrotate had long since 'deleted'. Deleted-but-open files consume disk invisible to du — the classic Unix trap, delivered by a shipper tuned for zero loss over bounded resource use.
Diagnosis Steps
Solution
Bounded the harvesters: close_timeout: 30m guarantees descriptor release regardless of shipping progress (accepting potential loss of the unshipped tail on pathological files — a documented tradeoff made explicitly), clean_removed and sane clean_inactive keep the registry honest, and the real fix — the downstream Logstash bottleneck — got capacity so harvesters stopped falling behind in the first place. Disk alerts now include a deleted-open-files check to name this failure instantly.
Commands
lsof -nP +L1 | awk '$NF ~ /deleted/' | sort -k7 -n | tail
filebeat config: close_timeout: 30m, clean_removed: true
curl -s localhost:5066/stats | jq '.filebeat.harvester.open_files'
Prevention
Any 'zero loss' shipper config must still bound resources: close_timeout is the backstop. Monitor Filebeat's open-handles count and registry size as regular metrics, alert on harvester lag (events behind), and teach the disk-full runbook the lsof-deleted check as step two.
💬 Comments