writing-plugins.md

Writing Munin Plugins

Protocol

A plugin is called with no arguments to fetch values, and with config to describe the graph. Optional: autoconf (print yes/no), suggest (print modes for wildcard plugins).

Config output

Global attributes describe the graph:

Attribute Purpose Example
graph_title Title above graph graph_title CPU usage
graph_vlabel Y-axis label graph_vlabel percent
graph_category Grouping in web UI graph_category system
graph_args Passed to rrdgraph graph_args --base 1000 -l 0
graph_scale Enable SI scaling graph_scale no
graph_info Description below graph graph_info CPU usage by type

Per-field attributes:

Attribute Purpose Example
field.label Legend label (required) cpu.label CPU
field.type GAUGE, COUNTER, DERIVE cpu.type DERIVE
field.draw LINE1, LINE2, AREA, AREASTACK mem.draw AREASTACK
field.min Minimum valid value cpu.min 0
field.max Maximum valid value cpu.max 100
field.warning Warning threshold cpu.warning 80
field.critical Critical threshold cpu.critical 95
field.info Field description cpu.info Percent CPU used
field.cdef RPN expression transform bytes.cdef bytes,8,*
field.negative Mirror another field below axis up.negative down

Value output

fieldname.value 42
otherfield.value 3.14

Use U for unknown: fieldname.value U

Wildcard plugins

A single plugin script handles multiple instances via its symlink name. The plugin parses basename $0 to determine what to monitor.

Example: if_ plugin symlinked as if_eth0, if_wlan0. The script strips its prefix to get the interface name.

Wildcard plugins should implement suggest to list valid instances:

if [ "${1:-}" = "suggest" ]; then
    ls /sys/class/net/ | grep -v '^lo$'
    exit 0
fi

Multigraph plugins

Emit multigraph <graph_id> before each graph's output:

#!/bin/sh
if [ "${1:-}" = "config" ]; then
    echo "multigraph service_users"
    echo "graph_title Users"
    echo "graph_category myservice"
    echo "graph_vlabel count"
    echo "total.label Total users"

    echo "multigraph service_uptime"
    echo "graph_title Uptime"
    echo "graph_category myservice"
    echo "graph_vlabel days"
    echo "uptime.label Uptime"
    exit 0
fi

echo "multigraph service_users"
echo "total.value $(get_user_count)"

echo "multigraph service_uptime"
echo "uptime.value $(get_uptime_days)"

The master must negotiate cap multigraph before list will show these plugins. This happens automatically during normal polling.

Graph args reference

Common graph_args values:

  • --base 1000: decimal SI units (1k = 1000)
  • --base 1024: binary units (1Ki = 1024), use for bytes
  • -l 0: lower limit 0 (graph won't go below)
  • --upper-limit 100: upper limit (for percentages)

Magic markers

Add these comments for munin-node-configure auto-detection:

#%# family=auto          # or contrib, manual
#%# capabilities=autoconf suggest

Testing during development

# Set MUNIN_LIBDIR if plugin uses plugin.sh helpers
export MUNIN_LIBDIR=/usr/share/munin   # or /usr/lib/munin on Arch

# Test directly
chmod +x ./myplugin
./myplugin config
./myplugin

# Test through munin-run (sets up full environment)
cp myplugin /etc/munin/plugins/
munin-run myplugin config
munin-run myplugin

Common patterns

Monitoring a CLI tool's output

#!/bin/sh
case ${1:-} in
    config)
        echo "graph_title My Service Stats"
        echo "graph_category myservice"
        echo "graph_vlabel count"
        echo "connections.label Active connections"
        exit 0 ;;
esac
echo "connections.value $(myservice-ctl status | awk '/connections:/ {print $2}')"

Monitoring an API endpoint

#!/bin/sh
case ${1:-} in
    config)
        echo "graph_title API Response Time"
        echo "graph_category network"
        echo "graph_vlabel ms"
        echo "response.label Response time"
        exit 0 ;;
esac
ms=$(curl -s -o /dev/null -w '%{time_total}' http://localhost:8080/health | awk '{printf "%.0f", $1 * 1000}')
echo "response.value $ms"

Monitoring container resource usage (Incus/LXD)

Query the Incus API via incus query for per-container stats. Run as user root in plugin-conf.d. Use DERIVE for CPU (cumulative nanoseconds → rate), AREASTACK for memory.