You pulled /api/plugins.json from the monitor_agent and one of your output plugins shows a nonzero retry_count, and it keeps going up. At the same time, write_count has stopped incrementing and buffer_queue_length is growing. Fluentd itself is alive and inputs are still collecting.
This is the destination-unavailable failure pattern: the output plugin cannot deliver chunks, the retry engine has taken over, and Fluentd is now in exponential backoff against a destination that is rejecting connections, rejecting data, or simply gone. Every event that arrives from now on accumulates in the buffer. The clock you are racing is buffer capacity, not the retry count itself.
The dangerous part is that this state can persist for a long time before anything looks “down.” The process is up. Inputs are up. Retries are nominally a recovery mechanism. But with exponential backoff, each failure pushes the next attempt further into the future, and a pipeline that is “retrying” can be effectively stalled for tens of minutes at a time.
What this means
When a chunk flush fails, the output plugin does not drop the chunk. It rolls the chunk back into the queue and the retry engine schedules another attempt. With the default retry_type exponential_backoff, the wait between attempts grows with each failure. If retry_max_times is not set and retry_forever is false, retries are bounded by retry_timeout (default 72 hours), after which the chunk is discarded: data loss, with only a log line to mark it.
Two things in the monitor_agent response describe this state, and they are not interchangeable:
retry_countis a cumulative counter of retry error occurrences on that output plugin. It tells you failures have happened, and whether the rate of failures is ongoing. In current Fluentd it does not reset to zero on success; it clears on process restart. (Some older documentation and builds describe it as resetting on a successful flush, so verify against your version before you alert on the raw value.)- The
retryobject (retry.start,retry.steps,retry.next_time) describes the live retry cycle: when it started, how many attempts have been made in this cycle, and when the next attempt fires. This is the field that tells you how bad things are right now.
That distinction matters operationally. A retry_count of 40 with no retry object present means past failures that have since recovered. A retry_count of 6 with retry.next_time 25 minutes in the future means the pipeline is stalled right now and getting worse.
flowchart LR A[Destination rejects or unreachable] --> B[Chunk flush fails] B --> C[Chunk rolled back to queue] C --> D[Retry scheduled with growing backoff] D -->|fails again| B D -->|succeeds| E[Queue drains] C --> F[buffer_queue_length grows] F --> G[Buffer approaches total_limit_size] G --> H[overflow_action: drop, block, or exception]
Common causes
| Cause | What it looks like | First thing to check |
|---|---|---|
| Destination down or unreachable | Connection refused, timeouts in Fluentd logs; write_count flat | Probe the destination directly from the Fluentd host |
| Destination overloaded or throttling | HTTP 429, slow responses; slow_flush_count and flush_time_count rising before retries started | Destination-side health (Elasticsearch cluster state, S3/Kafka throttling) |
| Authentication or TLS failure | 401/403, handshake errors, expired certificate or rotated credentials | Fluentd logs for auth/TLS error lines |
| Network partition or firewall change | Sudden onset across all outputs to one destination; no application-level error, just timeouts | Network path: DNS resolution, security groups, firewall rules |
| Schema or index conflict | Destination reachable but rejecting data (bulk requests rejected) | Destination logs; Fluentd logs for rejection responses |
| Permanent misconfiguration | Retries from the moment of a config change or deploy; never a single success | Recent config diff, endpoint, credentials in config |
Quick checks
All of these are read-only. Paths shown use the td-agent package layout; fluent-package uses /var/log/fluent/fluentd.log and /etc/fluent/fluentd.conf.
# 1. Per-output retry state and queue depth
curl -s http://localhost:24220/api/plugins.json | \
jq '.plugins[] | select(.plugin_category=="output") | {id: .plugin_id, retries: .retry_count, queue: .buffer_queue_length, writes: .write_count, avail_pct: .buffer_available_buffer_space_ratios}'
# 2. The live retry object (start, steps, next_time)
curl -s 'http://localhost:24220/api/plugins.json?with_retry=true' | \
jq '.plugins[] | select(.plugin_category=="output") | {id: .plugin_id, retry: .retry}'
# 3. Recent output errors from Fluentd's own log
grep -E "failed to flush|retry|temporarily failed|could not connect|broken pipe" \
/var/log/td-agent/td-agent.log | tail -20
# 4. Auth and TLS rejections
grep -iE "(tls|ssl|auth|401|403|unauthorized|forbidden|certificate)" \
/var/log/td-agent/td-agent.log | tail -20
# 5. Is the destination reachable at all from this host?
curl -s -o /dev/null -w "%{http_code}\n" --max-time 5 https://your-destination-endpoint/
Two notes on the checks above. First, the retry object is not guaranteed to be in the response: from v1.19.3 onward, monitor_agent’s include_retry defaults to false for security reasons. If the retry field is missing, set include_retry true in your monitor_agent source configuration rather than assuming there is no active retry. Second, in multi-worker mode each worker has its own monitor_agent port (24220, 24221, and so on): query every worker before concluding a retry is or is not happening.
How to diagnose it
Confirm the pattern, not just the counter. Take two samples of
retry_count,write_count, andbuffer_queue_lengtha minute apart. The active-failure signature is:retry_countincrementing,write_countflat,buffer_queue_lengthgrowing. Ifwrite_countis incrementing and the queue is draining, you are looking at a recovered failure and a stale cumulative counter, not an incident.Read the retry object.
retry.stepstells you how deep into the backoff curve this chunk is.retry.next_timetells you when Fluentd will try again. Ifnext_timeis more than a few minutes out, delivery latency is already severe and will keep growing even if you fix the destination this second, because the scheduled attempt is still far away.Get the actual error from the logs. The counters tell you that flushes fail; the log tells you why. Match the error to the cause table above: connection refused points at the destination or network, 401/403 at credentials, 429 at throttling, TLS errors at certificates. Auth errors often appear once at connection setup and are then masked by generic retry messages, so grep wider than the last few lines.
Verify the destination independently. From the Fluentd host, probe the destination endpoint and check the destination’s own health signals (Elasticsearch cluster state, Kafka broker status, S3 error rates). Distinguish “destination is down” from “destination is up but rejecting this data,” because the fixes are completely different.
Compute your runway. With the output stalled, the buffer fills at roughly the input rate. Check
buffer_available_buffer_space_ratiosand the growth ofbuffer_total_queued_sizeto estimate time to overflow. When the buffer hitstotal_limit_size,overflow_actionfires:throw_exception(the default) raises BufferOverflowError, which for most inputs means the new event is dropped;drop_oldest_chunkdiscards old data;blockstalls your inputs. None of these are good; all of them are worse than the retry state you are in now.
Metrics and signals to monitor
| Signal | Why it matters | Warning sign |
|---|---|---|
retry_count (rate, not raw value) | Confirms flush failures are ongoing | Delta > 0 sustained across samples |
retry.steps | Depth of the current backoff cycle | Large and growing; each step lengthens the wait |
retry.next_time | When delivery might resume | More than a few minutes in the future |
write_count | Whether any chunk is being delivered | Flat while input continues |
rollback_count | Chunks returned to the queue after failed flushes | Sustained nonzero rate |
buffer_queue_length | Backlog depth | Growing trend, not just high absolute value |
buffer_available_buffer_space_ratios | Proximity to overflow | Below 20% and falling |
flush_time_count / write_count | Average flush latency | Rising before retries start; the earliest warning |
slow_flush_count | Flushes over slow_flush_log_threshold (default 20s) | High ratio of slow to total flushes |
Fixes
Destination down or unreachable
Fix the destination; there is no Fluentd-side fix for a dead endpoint. While it is down, your job is to buy buffer time: verify buffer_available_buffer_space_ratios is draining slowly enough to outlast the outage, and confirm your overflow_action is the failure mode you actually want when the buffer fills. If you have a <secondary> output configured, chunks that exhaust retries fall through to it instead of being discarded; watch write_secondary_count.
Authentication or TLS failure
Rotate the credential or renew the certificate in the Fluentd configuration and reload. Auth failures never self-heal: retries against a bad credential just generate backoff. After the fix, see the note below about resetting retry state, because the backoff schedule does not care that you fixed the cause.
Throttling or overload (HTTP 429, quota exceeded)
Reduce pressure on the destination or slow Fluentd’s delivery. Check slow_flush_count and average flush time: if flushes were degrading before retries started, the destination was already at capacity. Longer term, the destination needs more capacity or the pipeline needs less volume; retry tuning alone does not fix a throughput deficit.
Recovering from deep backoff
This is the counterintuitive part. Once you fix the destination, chunks sitting deep in exponential backoff may have retry.next_time far in the future, so recovery is slow even though the cause is gone. The standard reset is to restart Fluentd: retry counters and backoff state reset on restart, and file-backed buffers replay their chunks from disk. Expect a flush burst and a temporarily high queue while the backlog drains. That burst is normal; do not mistake it for a new problem. Do not restart before fixing the underlying cause, or you will re-enter backoff from step one.
If you run many Fluentd instances (a Kubernetes DaemonSet, for example), make sure retry_randomize true is in effect. Without jitter, every instance retries on the same backoff schedule, and the synchronized retry burst can knock over a destination that just recovered, starting the cycle again.
Retry tuning
Adjust retry_max_interval to cap how long the backoff can grow, and be deliberate about retry_timeout (default 72 hours) versus retry_forever. A shorter timeout bounds how long stale chunks occupy the buffer but guarantees discard when it expires. retry_forever never discards, but then the buffer is your only bound, and overflow becomes the data-loss path instead. Neither is universally right; pick based on whether losing old data or blocking new data is worse for your pipeline.
Prevention
- Alert on the delta, not the value.
retry_countis cumulative and does not reliably return to zero. Alert ondelta(retry_count) > 0sustained, or better, on the presence of aretryobject withretry.next_timebeyond a threshold. - Pair retry alerts with buffer state. A retry alert without
buffer_queue_lengthandbuffer_available_buffer_space_ratioscontext cannot tell you how urgent it is. - Watch flush latency as the leading indicator. Rising
flush_time_count / write_countandslow_flush_countshow destination degradation before the first retry fires. - Choose
overflow_actionexplicitly per output. The defaultthrow_exceptiondrops new events at the buffer limit, and no counter reliably tracks those drops. - Use file-backed buffers in production. They survive restarts, which matters both for durability and for making the retry-state reset procedure safe.
- Configure a
<secondary>output for destinations where a multi-hour outage is plausible, so retry exhaustion routes to a fallback instead of the discard path.
How Netdata helps
- Netdata collects the Fluentd monitor_agent fields as time series, so
retry_count,write_count,rollback_count, andbuffer_queue_lengthappear on one timeline instead of in manual curl samples. The stall signature (retries up, writes flat, queue growing) is visible at a glance. - Because Netdata computes rates from cumulative counters, you alert on
delta(retry_count)rather than the raw value, which sidesteps the “counter never resets” trap. - Buffer saturation metrics (
buffer_available_buffer_space_ratios,buffer_total_queued_size) next to retry metrics let you estimate time-to-overflow while the destination is down. - Flush latency derived from
flush_time_count / write_countgives you the pre-retry degradation signal, so you see destination slowdowns before the first failure. - Per-plugin and per-worker breakdowns keep a single failing output, or a single struggling worker in multi-worker mode, from hiding inside aggregates.
Related guides
- Fluentd buffer queue length growing: the output cannot keep pace with the input
- Fluentd buffer available space low: computing time-to-overflow before it fires
- Fluentd BufferOverflowError: buffer space has too many data
- Fluentd drop_oldest_chunk_count incrementing: confirmed buffer data loss
- Fluentd emit_error_count: the number-one under-monitored data-loss signal
- Fluentd memory vs file buffer: why the default buffer loses data on restart
- Fluentd monitor_agent not responding: a process that is up but hung
- Fluentd monitoring checklist: the signals every production log pipeline needs
- How Fluentd actually works in production: a mental model for operators






