Fluentd fails to start, or starts with part of the pipeline missing, and the log shows LoadError, cannot load such file, or a load_plugin failure. If the failed plugin is an output, that destination silently receives no data while everything else looks healthy. If the plugin is critical to the config, the process refuses to start entirely and you find out via your process-alive check, or via a gap in downstream logs.
This failure almost always traces back to the Ruby gem layer: a plugin gem that is missing, installed into the wrong Ruby, built against the wrong Ruby version, or pinned to a version incompatible with the rest of the dependency tree. The classic trigger is an upgrade, of Fluentd itself, of the package (td-agent to fluent-package), or of the embedded Ruby, after which previously working plugins no longer load.
What this means
Fluentd plugins are Ruby gems. At startup, Fluentd reads the config, resolves each @type to a plugin, and requires the corresponding gem. That require can fail at several distinct layers, and the error text tells you which one:
- The gem is not installed at all in the Ruby environment Fluentd actually uses.
- The gem is installed, but for a different Ruby. The single most common cause: the operator ran
gem installwith the system Ruby instead offluent-gemortd-agent-gem, so the gem landed in a gem path Fluentd never searches. - The gem is installed but a dependency fails to require. You get a
LoadErrornaming some other file: a transitive dependency, a native extension, or a default gem that is not bundled. - A native (C) extension was compiled against a different Ruby. Ruby does not guarantee C extension compatibility across major versions. After the packaged Ruby is upgraded, every plugin with native extensions must be rebuilt.
- The plugin version is incompatible with the Fluentd core version. Plugins written against the v0.12 API do not work with Fluentd v1, and certain dependency bumps (such as the console gem) have broken plugin loading in specific Fluentd releases.
The blast radius depends on where the plugin sits. A failed input means that source is not collected. A failed output means that destination silently gets nothing. A failed parser or filter can break an entire match block. If Fluentd cannot construct a plugin the config requires, it aborts startup, which is when your process-dead alert fires.
flowchart TD
A[Startup log shows LoadError or load_plugin failure] --> B{Does the process stay up?}
B -->|No| C[Critical plugin failed - process-alive alert path]
B -->|Yes| D[Pipeline running degraded - one input or output dead]
C --> E[Identify plugin and error layer in logs]
D --> E
E --> F{Gem installed in Fluentd's Ruby?}
F -->|No| G[Reinstall with fluent-gem or td-agent-gem]
F -->|Yes| H{Dependency or native extension failing?}
H -->|Dependency| I[Pin or install the missing dependency gem]
H -->|Native ext| J[Rebuild against current Ruby - needs gcc, make, headers]
H -->|Version conflict| K[Pin compatible plugin and dependency versions]Common causes
| Cause | What it looks like | First thing to check |
|---|---|---|
Gem installed with system gem instead of the package’s gem wrapper | Plugin “installed” per gem list but Fluentd still cannot find it | Compare gem list against fluent-gem list or td-agent-gem list |
| Package upgrade removed plugins | Worked before a td-agent to fluent-package upgrade, now NotFoundPluginError or LoadError for a specific plugin | List installed plugin gems and diff against what the config references |
| Ruby version bump broke native extensions | LoadError naming a .so file or native extension after a package upgrade | Reinstall the plugin so the C extension rebuilds against the current Ruby |
| Incompatible dependency version | LoadError on a dependency file (console gem, elasticsearch transport) | Check release notes for your Fluentd version and pin the offending dependency |
| Plugin API mismatch (v0.12 era plugin) | Plugin fails with API errors on Fluentd v1 | Check the plugin’s documented Fluentd compatibility |
| Corrupt or unreadable plugin files | Permission denied @ rb_sysopen | Check permissions on the plugin gem directory |
| Missing build toolchain for native gems | ERROR: Failed to build gem native extension during install | Verify gcc, make, and development headers exist on the host |
| Minimal base image missing default gems | cannot load such file -- json (LoadError) on Alpine-based images | Check whether the distro’s Ruby package bundles default gems like json |
Quick checks
All read-only. Paths shown are for td-agent; for fluent-package, substitute fluentd for td-agent in unit names, /var/log/fluent/fluentd.log for the log path, and /etc/fluent/ for the config path.
# 1. Find the actual load error in startup logs
journalctl -u td-agent --since "30 minutes ago" | \
grep -iE "(load_plugin|LoadError|uncaught|cannot load)"
# 2. Confirm whether the process survived
systemctl status td-agent
ps aux | grep '[f]luentd'
# 3. List plugins visible to Fluentd's own Ruby (td-agent)
td-agent-gem list | grep fluent-plugin
# 3b. fluent-package equivalent
fluent-gem list | grep fluent-plugin
# 4. Compare with what the system Ruby sees (common mismatch)
gem list | grep fluent-plugin
# 5. Which plugins does the config actually reference?
grep -E "@type" /etc/td-agent/td-agent.conf | sort -u
# 6. Check what plugins the running process loaded (if monitor_agent is up)
curl -s http://localhost:24220/api/plugins.json | \
jq -r '.plugins[] | "\(.plugin_category)\t\(.type)\t\(.plugin_id)"'
Check 4 catches the most common cause: if gem list shows the plugin but td-agent-gem list (or fluent-gem list) does not, you installed into the wrong Ruby. Check 6 tells you what actually loaded: if the process is up but a <match> plugin is absent from the list, that output is dead and its destination is receiving nothing.
How to diagnose it
Capture the exact error. Run the journalctl grep from the quick checks, or grep the log file directly:
grep -iE "(load_plugin|LoadError|cannot load)" /var/log/td-agent/td-agent.log. Note three things: the plugin name, the file Ruby failed to require, and the error class.cannot load such file -- fluent/plugin/out_foopoints at the plugin gem itself.cannot load such file -- some_dependencypoints at a transitive dependency inside an installed gem.Determine the blast radius. Is the process running (
systemctl status)? If it is up, the failure was non-fatal and one branch of the pipeline is dark. Cross-reference the failed plugin against your config: is it an input (source lost), a filter (records flow unfiltered or the match fails), or an output (destination starved)? If the process is down, this is the process-alive incident path; see Fluentd process not running.Verify installation against the correct Ruby. Fluentd packages ship an embedded Ruby with its own gem path. For td-agent that Ruby lives under
/opt/td-agent/embedded/and gems are managed withtd-agent-gem; for fluent-package, usefluent-gem. Run the package’s gem wrapper and confirm the plugin and its version are present. If you historically installed with baregem, assume the gem went to the system Ruby and reinstall with the wrapper.If the gem is present, check the dependency layer. A
LoadErroron a file that is not the plugin itself means an installed gem has a broken or missing dependency. Two documented cases: console gem v1.25 and later caused a LoadError that broke plugins such as fluent-plugin-prometheus, fixed in Fluentd v1.16.6 and v1.17.1; and Elasticsearch 8 removed an internal transport module, breaking fluent-plugin-elasticsearch unless the elasticsearch gem is pinned to v7.17.0 with fluent-plugin-elasticsearch v5.0.3. Check your Fluentd version against its release notes before pinning blindly.If the error names a native extension, suspect a Ruby upgrade. C extensions are compiled against a specific Ruby ABI. When the package upgrades its embedded Ruby, previously compiled extensions fail to load. Reinstalling the plugin with the package’s gem wrapper forces a rebuild. If the build itself fails with
Failed to build gem native extension, the host is missinggcc,make, or the Ruby development headers.Check file permissions on the plugin files. A gem installed as root with a restrictive umask can leave plugin files unreadable by the Fluentd service user, producing
Permission denied @ rb_sysopen. Verify the service user can read the plugin gem directory and files (755directories,644files are the norm for gem installations).Confirm the fix end to end. After reinstalling or pinning, restart the service and re-run the startup grep plus the monitor_agent plugin listing. Then verify data flow, not just startup: an output that was starved during the outage will drain its buffer backlog, which looks like a flush spike. That is normal replay behavior.
Metrics and signals to monitor
| Signal | Why it matters | Warning sign |
|---|---|---|
| Plugin load errors in startup logs | Direct detection of this failure; binary signal | Any match on load_plugin, LoadError, cannot load at startup |
| Process alive | A critical plugin failure aborts startup entirely | Process absent or crash-looping after a deploy or package upgrade |
| Plugin inventory via monitor_agent | Tells you which plugins loaded vs. what the config expects | A configured input or output missing from /api/plugins.json |
| Input emit_records rate per plugin | A silently dead input shows up as throughput dropping to zero | Sustained deviation from baseline, or zero with the source active |
| Output emit_records vs. input rate | A silently dead output shows up as sustained input/output divergence | Output rate flatlines while input rate stays normal |
| Buffer queue length and oldest timekey | If an output fails to load, chunks destined for it stop draining | Queue growth and aging data immediately after a restart or upgrade |
The subtle case is the non-fatal one: process up, no page, one destination dark. The only reliable detectors are comparing the loaded-plugin inventory against the config, and watching per-plugin throughput for a branch that went to zero. Once a starved output’s buffer hits its limit, the outcome depends on overflow_action and Fluentd version, and none of the outcomes is a clean, obvious error counter. Detecting the load failure itself is the real defense.
Fixes
Reinstall the plugin into the correct Ruby
# td-agent
sudo td-agent-gem install fluent-plugin-<name>
# fluent-package
sudo fluent-gem install fluent-plugin-<name>
Never use bare gem install for Fluentd plugins on packaged installs; it targets the system Ruby and the gem will be invisible to Fluentd. After installing, restart the service and verify with the package gem wrapper’s list command and the monitor_agent inventory. A restart briefly interrupts ingestion, so plan for it on busy pipelines.
Reinstall plugins after a package upgrade
Treat plugin reinstallation as a mandatory step of any td-agent or fluent-package upgrade, not an afterthought. Upgrades between package families (td-agent v4 to fluent-package v5 or v6) are known to drop previously installed plugins, and upgrades that change the embedded Ruby invalidate native extensions. Before upgrading, inventory what you have:
# Snapshot installed plugins before upgrade
td-agent-gem list | grep fluent-plugin > /root/fluent-plugins-before.txt
After the upgrade, reinstall everything in the snapshot with the new package’s gem wrapper, then restart and verify. fluent-package v5.0.2 and later ships fluent-diagtool, which can help enumerate manually installed plugins.
Pin compatible dependency versions
When the LoadError names a dependency rather than the plugin, pin the working versions instead of taking the latest:
- For the console gem breakage, upgrade Fluentd to a release that contains the fix.
- For Elasticsearch 8 destinations, pin the elasticsearch gem to v7.17.0 and fluent-plugin-elasticsearch to v5.0.3.
The maintainable way to pin versions in production is a Bundler Gemfile, so every install and upgrade resolves the same locked dependency set.
Fix native extension build failures
If installation fails at build time, install the toolchain first, then retry the gem install with the package wrapper. On Debian-family systems that means gcc, make, and the Ruby development headers; on minimal container images you may need a full build base. In Docker images, install gems as root and switch back to the fluent user afterward.
Fix unreadable plugin files
If the error is Permission denied @ rb_sysopen, correct permissions on the affected gem files so the Fluentd service user can read them, then restart. Check for this after any manual gem install done as root with a restrictive umask.
Missing default gems on minimal images
On Alpine-based images, the minimal Ruby package may not include default gems such as json, producing cannot load such file -- json (LoadError). Install the missing gem explicitly in the image build.
Prevention
- Always use the package gem wrapper. Standardize on
fluent-gemortd-agent-gemin runbooks, configuration management, and image builds. The wrong-Ruby install is the most common cause and the easiest to prevent. - Manage plugin versions as code. Use a locked Gemfile so plugin and dependency versions are pinned, reviewable, and reproducible across hosts and rebuilds.
- Make plugin reinstallation part of the upgrade runbook. Snapshot
gem listbefore, reinstall after, and verify the loaded-plugin inventory against the config before declaring the upgrade done. - Test startup in staging with the production config. A config that references a plugin missing from the image fails exactly the same way in staging as in production. Boot the service, grep for load errors, and diff the monitor_agent plugin list.
- Monitor startup logs, not just process state. A process that starts degraded (one plugin failed) passes a process-alive check. Alert on any
LoadErrororload_pluginmatch in startup logs. - Keep Fluentd current. Dependency-related LoadErrors (console gem, json parser) were fixed in specific releases; running an affected version means re-discovering known bugs. fluent-package v5 LTS and td-agent v4 are past or near end of life, so plan migrations to fluent-package v6 LTS.
How Netdata helps
- Netdata’s process and systemd unit monitoring catches the fatal case immediately: the Fluentd process missing or crash-looping after a plugin failure aborts startup, correlated with restart counts so a flapping service is visible instead of masked by auto-restart.
- Per-second host metrics let you correlate the plugin failure with its trigger: the restart, deploy, or package upgrade that preceded the LoadError shows up on the same timeline as CPU, memory, and disk activity.
- Netdata can tail and alert on Fluentd’s own log file, so a
LoadErrororcannot loadpattern at startup fires as an alert rather than sitting unread in journald. - Combined with Fluentd’s monitor_agent endpoint, Netdata charts per-plugin buffer queue length, retry count, and emit rates, which surfaces the degraded case: process alive but one output’s throughput flatlined after a restart.
- Long retention on these signals makes post-upgrade verification practical: you can compare per-plugin throughput before and after the upgrade window and confirm every branch recovered.






