The application is writing logs. ls -la shows the file growing. But Fluentd’s input emit_records for that in_tail source has been flat for minutes or hours, and nothing is arriving downstream. The daemon runs, the buffers stay empty, and the only evidence is missing data. This is an input-side stall, and it is one of the quieter Fluentd failures because nothing page-worthy breaks.
A restart forces file re-discovery and usually restores flow temporarily, which is exactly why this failure gets misdiagnosed: the restart masks the cause, and the stall returns at the next rotation or the next burst of new files.
Severity: a pos_file position that is not advancing at all is PAGE-level, because Fluentd is effectively dead for that source while appearing alive. A position that trails the file size but keeps advancing is a TICKET-level lag problem.
What this means
in_tail keeps one open file descriptor and one position entry per tailed file. The position file (pos_file) records where Fluentd stopped reading, so it can resume without re-reading or skipping after a restart. For the pipeline to advance, three things must all be true:
- The file must match the
pathglob and be discovered by the watcher (inotify or stat-based, depending on configuration). - Fluentd must be able to open and hold an FD for it.
- Reads must succeed and the pos entry must advance.
A stall means one of those broke. The file growing on disk only proves the application side is fine. Common breakpoints: the watcher lost the file at rotation, the glob stopped matching after a path change, the pos entry desynced from the real file, FD exhaustion blocked new opens, or the input thread is paused by buffer backpressure. The official in_tail FAQ notes that when in_tail receives BufferOverflowError, it stops reading new lines and updating the pos_file until the buffer error clears; with overflow_action block, the input thread blocks outright.
One deployment constraint matters before anything else: in_tail does not support multi-worker mode. It must be pinned to a single worker with <worker N>, and its metrics only appear on that worker’s monitor_agent port (24220 + worker_id). If you are checking the wrong port, emit_records will look flat because you are looking at a worker that never ran the input.
flowchart TD
A["Log file growing, emit_records flat"] --> B{"Pos file advancing?"}
B -->|"Yes, but lagging"| C["Read throughput problem: CPU/GVL-bound parser or throttle"]
B -->|"No, stuck"| D{"tracked_file_count matches expectation?"}
D -->|"Count dropped"| E["Glob/path mismatch, file deleted, or FD exhaustion"]
D -->|"Count OK"| F{"Rotation involved?"}
F -->|"Yes"| G["Rotation handling failure: copytruncate race, rotate_wait, watcher stall bug"]
F -->|"No"| H["Buffer backpressure blocking input, or hung watcher thread"]Common causes
| Cause | What it looks like | First thing to check |
|---|---|---|
| Rotation handling failure | Stall starts right at the logrotate window; rotated_file_count stops incrementing on a host that rotates daily | Compare inode in pos_file with stat -c %i on the live file |
| Watcher stall bug (older versions) | After rotation, tailing stops entirely; log may show “unreadable. It is excluded and would be examined next time.” | Fluentd version; v1.16.2/v1.16.3 fixed years-old stop-tailing bugs |
rotate_wait larger than refresh_interval | Reproducible stalls around rotation | Config: the v1.16.3 release notes state this combination “will surely cause this issue” on older versions |
| Glob stopped matching | tracked_file_count (v1.19.0+) dropped; new files never discovered | Does path still match the real files, including symlink targets? |
| FD exhaustion | New files never opened; possibly “too many open files” errors, but in_tail can also fail silently | FD count vs ulimit -Sn |
| pos_file desync or loss | Positions reset to zero (duplicates) or jump past data (gaps); pos_file on tmpfs lost at reboot | pos_file contents, mtime, and mount point |
| Buffer backpressure | Stall coincides with full buffer; input thread blocked (overflow_action block) or BufferOverflowError pausing reads | buffer_available_buffer_space_ratios, buffer_queue_length on outputs |
| enable_input_metrics off (older versions) | emit_records is zero but data is actually flowing | Fluentd < v1.19.0 requires enable_input_metrics true in <system>; verify at the destination before assuming a stall |
That last row is the cheap check everyone skips: on Fluentd before v1.19.0, input emit_records requires enable_input_metrics true in <system>. Without it, the counter is always 0 and the pipeline looks stalled when it is not. Before declaring an incident, confirm the destination is actually missing data.
Quick checks
All read-only. Paths shown for td-agent; fluent-package uses /var/log/fluent/fluentd.log and /etc/fluent/fluentd.conf.
# 1. Confirm the file is really growing
ls -la /var/log/app/app.log
stat -c '%s %i %Y' /var/log/app/app.log # size, inode, mtime
# 2. Check input emit_records on the CORRECT worker port
curl -s http://localhost:24220/api/plugins.json | \
jq '.plugins[] | select(.type=="tail") | {id: .plugin_id, emit: .emit_records}'
# 3. Check tracked and rotated file counts (tracked_file_count needs v1.19.0+)
curl -s http://localhost:24220/api/plugins.json | \
jq '.plugins[] | select(.type=="tail") | {id: .plugin_id, tracked: .tracked_file_count, rotated: .rotated_file_count}'
# 4. Read the pos_file and compare position against file size
grep pos_file /etc/td-agent/td-agent.conf
cat /var/log/td-agent/td-agent.pos
# Each line records the watched path, the read position (hex), and the inode (hex).
ls -la /var/log/td-agent/td-agent.pos # mtime should be recent while tailing is live
# 5. Check file descriptor usage against the limit
ls /proc/$(pgrep -f fluentd | head -1)/fd | wc -l
grep "Max open files" /proc/$(pgrep -f fluentd | head -1)/limits
# 6. Look for watcher and rotation messages in Fluentd's own log
grep -iE "unreadable|Skip update_watcher|detected rotation|too many open files|BufferOverflowError" \
/var/log/td-agent/td-agent.log | tail -30
# 7. Check whether outputs are full and pushing back on the input
curl -s http://localhost:24220/api/plugins.json | \
jq '.plugins[] | select(.plugin_category=="output") | {id: .plugin_id, avail_pct: .buffer_available_buffer_space_ratios, queue: .buffer_queue_length}'
# 8. Confirm Fluentd version (several stall causes are version-specific)
fluentd --version
How to diagnose it
Rule out instrumentation blindness first. If Fluentd is older than v1.19.0 and
<system>lacksenable_input_metrics true, inputemit_recordsis always 0. Check the destination for recent events. If data is flowing, your problem is monitoring, not tailing. See enable_input_metrics on older versions.Verify you are querying the right worker. In multi-worker setups
in_tailis pinned via<worker N>and its metrics only exist on that worker’s port. Query 24220 + worker_id.Check whether the pos entry is advancing at all. Note the position for the growing file, wait 60 seconds, check again. Advancing but behind: read-throughput problem (CPU-bound parser, throttling, or GVL contention). Not advancing: continue.
Check
tracked_file_countand the glob. On v1.19.0+, a droppedtracked_file_countmeans files vanished from the watch set: the path changed, the glob no longer matches, or FDs are exhausted. On older versions, compareopened_file_countandclosed_file_counttrends. In Kubernetes, remember/var/log/containers/*.logentries are symlinks; if the runtime changed its log path layout (for example Docker to containerd), the pattern breaks silently.Check FD headroom. If FD count is near the soft limit, new files cannot be opened.
in_taildoes not always log an explicit EMFILE error; it can just stop picking up new files. If this is the cause, raise the limit and reduce the watch set.Correlate the stall start time with rotation. If the flatline began at the logrotate window, compare the inode recorded in the pos_file with
stat -c %ion the live file. A persistent mismatch pastrotate_wait(default 5s) means the watcher lost the file. Also check Fluentd’s log: the line “unreadable. It is excluded and would be examined next time.” followed by no recovery is the canonical signature of the watcher stall (GitHub issue #3614 ), and v1.15.1+ emits “Skip update_watcher because watcher has been already updated by other inotify event” when it happens. If the “detected rotation of …” line never appears for a file you know rotated, detection itself failed.Check version against known stall bugs. The stop-tailing failure existed for years and was fixed in v1.16.2/v1.16.3 for both
follow_inodes trueandfalsecases. The v1.14.3 release fixed a full read stop withenable_watch_timer falseplusenable_stat_watcher true(the default), and the v1.14.3 notes recommend at least v1.12.4 for anyone on the v1.12 line because of serious in_tail bugs. On affected versions, upgrading is the fix, not config tuning.Check for backpressure. If
buffer_available_buffer_space_ratiosis near 0 on any output, the input may be blocked byoverflow_action blockor paused byBufferOverflowError. The stall is then a symptom of an output problem; diagnose the output side (see related guides below).
Metrics and signals to monitor
| Signal | Why it matters | Warning sign |
|---|---|---|
Input emit_records rate (per in_tail plugin) | Direct proof that tailing is alive | Flat while the source file grows; requires enable_input_metrics true before v1.19.0 |
tracked_file_count (v1.19.0+) | Current number of files being tailed | Unexpected drop: glob mismatch, deleted files, or FD exhaustion |
rotated_file_count | Confirms rotation detection is working | Stops incrementing on a host with scheduled rotations |
| Pos_file position vs file size | Ground truth for read progress | Position static, or lag growing monotonically |
| Pos_file mtime | Cheap liveness check on the tail loop | Stale beyond a flush cycle while files are active |
FD count vs ulimit -Sn | in_tail holds one FD per file; exhaustion blocks new opens | Above 75% of the soft limit |
buffer_available_buffer_space_ratios (outputs) | Full buffers block or pause the input | Under 20% and still falling |
Fixes
Rotation and watcher stalls
- Upgrade to v1.16.3 or later. This is the durable fix for the stop-tailing bug that survives restarts and recurs at every rotation. If you are pinned to an older version, keep
rotate_waitat or belowrefresh_interval; the release notes state that a largerrotate_waitreliably triggers the stall. - Prefer rename/create rotation over copytruncate. Copytruncate has an inherent race: events written between the copy and the truncate can be missed. If copytruncate is unavoidable, treat some loss at the truncation window as a known limitation.
- Size
rotate_waitandrefresh_intervalcorrectly.rotate_wait(default 5s) must cover the gap between the rename and the new file appearing. A too-longrefresh_intervaldelays discovery of the new file after rotation. - Use
follow_inodes truewith wildcard paths under rotation (v1.12.0+) to avoid duplicate reads when*matches both old and new files. - If you tail many files and hit the “stuck” behavior, the official in_tail FAQ recommends
enable_stat_watcher falseas a workaround. Tradeoff: rotation detection then relies on the watch timer and refresh cycle rather than stat watching; validate rotation behavior after changing it. - Do not set
enable_watch_timer falsecasually. Combined with the default stat watcher it caused full read stops on v1.14.2 and earlier, and with multiline parsers it preventsmultiline_flush_intervalfrom working, so the last record in a file is never emitted.
Glob and path problems
- Fix the
pathglob to match reality, including symlink targets in Kubernetes. If you need extended glob syntax ([],?,{}),glob_policy extendedis available in v1.17.0 for bothpathandexclude_path. - After fixing the pattern, restart to force re-discovery, and set
read_from_head true(temporarily, if needed) to backfill files created during the blind window. Since v1.14.3, newly discovered files are read from head automatically even withread_from_head false, but on older versions initial lines of new files could be skipped with wildcard paths.
FD exhaustion
- Raise the soft limit in the systemd unit or container security context; production deployments commonly need 65536 or more. Budget: tailed files + buffer chunk files + output connections + baseline.
- Reduce the watch set: narrow globs, exclude rotated archives with
exclude_path, and split sources across workers or agents if the file count is genuinely large.
pos_file problems
- Keep the pos_file on a persistent, local filesystem. On tmpfs it is lost at reboot and every file is re-read (with
read_from_head true) or skipped. The maintainers have stated pos_file is not designed for NFS; aggressivepos_file_compaction_intervalvalues on NFS have caused entries to be deleted and files re-read from the beginning with “Unparsable line in pos_file” warnings. - Use
pos_file_compaction_intervalwhen tailing many files with dynamic paths, so the pos_file does not grow unbounded. - If positions are corrupted, the controlled recovery is: stop Fluentd, fix or remove the stale entries, restart. Editing a live pos_file while Fluentd runs is not supported.
Backpressure-induced stalls
- The tail stall is downstream of a buffer problem. Fix the destination or resize the buffer; see buffer queue length growing and failed to flush the buffer. Choose
overflow_actiondeliberately:blockprotects data but stalls inputs,throw_exception(the default) raisesBufferOverflowError, which pauses in_tail reads and drops events from push-style inputs with a logged error, anddrop_oldest_chunkdiscards old data.
Prevention
- Alert on the input side, not just outputs. A flat input
emit_recordsrate on an active source is PAGE-worthy. Do not rely on process liveness; a running Fluentd with a stalled tail is a blind spot. - Enable
enable_input_metrics truein<system>on anything older than v1.19.0, or you will not be able to see input stalls at all. - Track
tracked_file_countandrotated_file_count(version permitting) against expectations per host. A rotation counter that stops incrementing on a host with daily logrotate is an early warning. - Monitor FD usage against the soft limit and alert at 75%.
- Test rotation before production. Run logrotate against a tailed file and verify the pos entry follows to the new inode with no gap and no duplicates. Most in_tail stalls only appear at rotation.
- Pin Fluentd to a version with the in_tail fixes (v1.16.3+) and treat
enable_watch_timer falseandrotate_wait > refresh_intervalas review flags in config changes. - Keep pos_file on persistent local disk, and include it in any host rebuild or container volume planning.
How Netdata helps
- Netdata charts Fluentd’s monitor_agent metrics per plugin, so a flat input
emit_recordsline next to a growing output-side gap is visible in one dashboard instead of two manualjqqueries. - Per-plugin breakdowns show which
in_tailsource stalled when you tail dozens of files, rather than forcing you to reason from an aggregate. - Correlating input rate with
buffer_available_buffer_space_ratiosandbuffer_queue_lengthdistinguishes a true tail stall from backpressure pausing the input, which changes the fix entirely. rotated_file_countandtracked_file_counttrends make rotation-related stalls obvious: the flatline lines up exactly with the rotation window.- Host-level FD and RSS metrics sit on the same timeline as Fluentd’s internal counters, so FD exhaustion shows up as a rising resource line converging with the input stall.
Related guides
- Fluentd input emit_records stuck at zero: enable_input_metrics on older versions
- Fluentd failed to flush the buffer: the output cannot deliver and retries begin
- Fluentd buffer queue length growing: the output cannot keep pace with the input
- Fluentd BufferOverflowError: buffer space has too many data
- Fluentd emit_error_count: the number-one under-monitored data-loss signal
- Fluentd config reload failed: SIGHUP that partially applies
- Fluentd CrashLoopBackOff: rapid restart cycling in Kubernetes
- How Fluentd actually works in production: a mental model for operators






