src/plugins.d/DYNCFG.md
External plugins in Netdata can expose dynamic configuration capabilities through the DynCfg system. This document explains how to implement DynCfg in external plugins using the plugins.d protocol.
The DynCfg system allows external plugins to:
DynCfg for external plugins uses the following plugins.d protocol commands:
CONFIG: Sent from the plugin to Netdata to register, update status, or delete configurationsFUNCTION/FUNCTION_PAYLOAD_BEGIN: Received by the plugin to handle configuration commandsFUNCTION_RESULT_BEGIN: Sent from the plugin to respond to commandsTo register a configuration, the plugin sends the CONFIG command:
CONFIG <id> CREATE <status> <type> <path> <source_type> <source> <cmds> <view_access> <edit_access>
Where:
id is a unique identifier for the configurable entity (e.g., "go.d:nginx")status can be:
accepted: Configuration is accepted but not runningrunning: Configuration is accepted and runningfailed: Plugin fails to run the configurationincomplete: Plugin needs additional settingsdisabled: Configuration is disabled by a usertype can be:
single: A single configuration object (not addable or removable by users)template: A template for creating multiple job configurationsjob: A specific job configuration (derived from a template)path is the UI organization path (usually "/collectors") that determines where in the configuration tree the item will appear in the UI. This is separate from the ID and controls the hierarchical navigation structure.source_type can be:
internal: Based on internal code settingsstock: Default configurationsuser: User configurations via a filedyncfg: Configuration received via this mechanismdiscovered: Dynamically discovered by the pluginsource provides more details about the exact sourcecmds is a space or pipe (|) separated list of supported commands:
schema: Get JSON schema for the configurationget: Get current configuration valuesupdate: Receive configuration updatesadd: Receive job creation commands (templates only)remove: Remove a configuration (jobs only)enable/disable: Enable or disable the configurationtest: Test a configuration without applying itrestart: Restart the configurationuserconfig: Get user-friendly configuration formatview_access and edit_access are permission bitmaps (use 0 for default permissions)Example:
CONFIG go.d:nginx CREATE accepted template /collectors internal internal schema|add|enable|disable 0 0
CONFIG go.d:nginx:local_server CREATE running job /collectors dyncfg user schema|get|update|remove|enable|disable|restart 0 0
The plugin receives configuration commands from Netdata as plugin functions. These come in two forms:
FUNCTION <transaction_id> <timeout_ms> "config <id> <command>" "<http_access>" "<source>"
Used for commands like: schema, get, remove, enable, disable, restart
Example:
FUNCTION abcd1234 60 "config go.d:nginx:local_server get" "member" "netdata-cli"
FUNCTION_PAYLOAD_BEGIN <transaction_id> <timeout_ms> "config <id> <command>" "<http_access>" "<source>" "<content_type>"
<payload_data>
FUNCTION_PAYLOAD_END
Used for commands like: update, add, test that require additional data.
Example:
FUNCTION_PAYLOAD_BEGIN abcd1234 60 "config go.d:nginx:local_server update" "member" "netdata-cli" "application/json"
{
"url": "http://localhost:80/stub_status",
"timeout": 5,
"update_every": 10
}
FUNCTION_PAYLOAD_END
After receiving a command, the plugin should process it and respond with a function result:
FUNCTION_RESULT_BEGIN <transaction_id> <http_status_code> <content_type> <expiration>
<result_data>
FUNCTION_RESULT_END
Where:
transaction_id is the same ID received in the original commandhttp_status_code is the standard HTTP response code:
200: Success (DYNCFG_RESP_RUNNING) - Configuration accepted and running202: Accepted (DYNCFG_RESP_ACCEPTED) - Configuration accepted but not running yet298: Accepted but disabled (DYNCFG_RESP_ACCEPTED_DISABLED)299: Accepted but restart required (DYNCFG_RESP_ACCEPTED_RESTART_REQUIRED)400: Bad request - Invalid configuration404: Not found - Configuration not found500: Internal server errorcontent_type is typically "application/json"expiration is the absolute timestamp (unix epoch) for result expirationThe result data depends on the command:
schema: Return JSON Schema documentget: Return current configuration valuesSuccess response example:
FUNCTION_RESULT_BEGIN abcd1234 200 application/json 0
{
"status": 200,
"message": "Configuration updated successfully"
}
FUNCTION_RESULT_END
Error response example:
FUNCTION_RESULT_BEGIN abcd1234 400 application/json 0
{
"status": 400,
"error_message": "Invalid URL format"
}
FUNCTION_RESULT_END
To update the status of a configuration after it's been created:
CONFIG <id> STATUS <new_status>
Example:
CONFIG go.d:nginx:local_server STATUS running
This is useful when a configuration transitions from "accepted" to "running" or "failed" after being tested.
When a configuration is no longer available (e.g., the monitored service is removed):
CONFIG <id> DELETE
Example:
CONFIG go.d:nginx:local_server DELETE
DynCfg uses JSON Schema to define the structure of configuration objects, which is used to generate the UI.
Before calling the plugin, Netdata will first attempt to find a static schema file. You can provide static schema files in:
CONFIG_DIR/schema.d/ (user-provided schemas, typically /etc/netdata/schema.d/)LIBCONFIG_DIR/schema.d/ (stock schemas, typically /usr/lib/netdata/conf.d/schema.d/)Schema files should be named after the configuration ID with .json extension:
/etc/netdata/schema.d/go.d:nginx.json
This approach is useful for stable schemas that don't change frequently.
If no static schema file is found, Netdata will send a schema command to the plugin. When handling a schema request, the plugin should return a JSON Schema document:
{
"type": "object",
"properties": {
"url": {
"type": "string",
"format": "uri",
"title": "Server URL",
"description": "The URL of the Nginx stub_status endpoint"
},
"timeout": {
"type": "integer",
"minimum": 1,
"maximum": 60,
"title": "Timeout",
"description": "Connection timeout in seconds"
},
"update_every": {
"type": "integer",
"minimum": 1,
"title": "Update Every",
"description": "Data collection frequency in seconds"
}
},
"required": [
"url"
]
}
For templates, the schema will be used when users add new jobs based on the template.
When implementing DynCfg in your external plugin, be aware of how actions should behave based on the configuration type:
| Action | TEMPLATE | JOB |
|---|---|---|
| SCHEMA | Return schema for creating new jobs | Use template's schema |
| GET | Not applicable | Return current configuration |
| UPDATE | Not applicable | Update configuration and apply if valid |
| ADD | Create new job from template | Not applicable |
| REMOVE | Not supported | Remove job (only for user-created jobs) |
| ENABLE | Enable template and all its jobs | Enable specific job |
| DISABLE | Disable template and all its jobs | Disable specific job |
| RESTART | Restart all jobs based on template | Restart specific job |
| TEST | Test a potential job configuration | Test configuration changes |
| USERCONFIG | Return template in user-friendly format | Return job in user-friendly format |
Important Implementation Notes:
The systemd-journal.plugin is a C-based external plugin that uses DynCfg to manage journal directory configurations. It implements a SINGLE configuration type to manage the list of journald directories to monitor:
// Register the configuration
functions_evloop_dyncfg_add(
wg,
"systemd-journal:monitored-directories", // ID
"/logs/systemd-journal", // UI Path
DYNCFG_STATUS_RUNNING, // Status
DYNCFG_TYPE_SINGLE, // Type - single configuration
DYNCFG_SOURCE_TYPE_INTERNAL, // Source type
"internal", // Source
DYNCFG_CMD_SCHEMA | DYNCFG_CMD_GET | DYNCFG_CMD_UPDATE, // Supported commands
HTTP_ACCESS_NONE, // View permissions
HTTP_ACCESS_NONE, // Edit permissions
systemd_journal_directories_dyncfg_cb, // Callback function
NULL // User data
);
Key points about its implementation:
Here's a complete example showing how a Go-based external plugin might implement DynCfg for an Nginx module:
# Register the template for Nginx configurations
CONFIG go.d:nginx CREATE accepted template /collectors internal internal schema|add|enable|disable 0 0
# Register existing jobs
CONFIG go.d:nginx:local_server CREATE running job /collectors user /etc/netdata/go.d/nginx.conf schema|get|update|remove|enable|disable|restart 0 0
CONFIG go.d:nginx:production CREATE running job /collectors user /etc/netdata/go.d/nginx.conf schema|get|update|remove|enable|disable|restart 0 0
When receiving:
FUNCTION abcd1234 60 "config go.d:nginx schema" "member" "netdata-cli"
Respond with:
FUNCTION_RESULT_BEGIN abcd1234 200 application/json 0
{
"type": "object",
"properties": {
"url": {
"type": "string",
"format": "uri",
"title": "Server URL",
"description": "The URL of the Nginx stub_status endpoint"
},
"timeout": {
"type": "integer",
"minimum": 1,
"maximum": 60,
"title": "Timeout",
"description": "Connection timeout in seconds"
},
"update_every": {
"type": "integer",
"minimum": 1,
"title": "Update Every",
"description": "Data collection frequency in seconds"
}
},
"required": ["url"]
}
FUNCTION_RESULT_END
When receiving:
FUNCTION abcd1234 60 "config go.d:nginx:local_server get" "member" "netdata-cli"
Respond with:
FUNCTION_RESULT_BEGIN abcd1234 200 application/json 0
{
"url": "http://localhost:80/stub_status",
"timeout": 5,
"update_every": 10
}
FUNCTION_RESULT_END
When receiving:
FUNCTION_PAYLOAD_BEGIN abcd1234 60 "config go.d:nginx:local_server update" "member" "netdata-cli" "application/json"
{
"url": "http://localhost:8080/stub_status",
"timeout": 3,
"update_every": 5
}
FUNCTION_PAYLOAD_END
Process the update and respond:
FUNCTION_RESULT_BEGIN abcd1234 200 application/json 0
{
"status": 200,
"message": "Configuration updated successfully"
}
FUNCTION_RESULT_END
If a restart is required:
FUNCTION_RESULT_BEGIN abcd1234 299 application/json 0
{
"status": 299,
"message": "Configuration updated, restart required to apply changes"
}
FUNCTION_RESULT_END
When receiving:
FUNCTION_PAYLOAD_BEGIN abcd1234 60 "config go.d:nginx add" "member" "netdata-cli" "application/json"
{
"name": "staging",
"url": "http://staging:80/stub_status",
"timeout": 5,
"update_every": 10
}
FUNCTION_PAYLOAD_END
Process the new job and respond:
FUNCTION_RESULT_BEGIN abcd1234 200 application/json 0
{
"status": 200,
"message": "Job 'staging' created successfully"
}
FUNCTION_RESULT_END
Then register the new job:
CONFIG go.d:nginx:staging CREATE running job /collectors dyncfg netdata-cli schema|get|update|remove|enable|disable|restart 0 0
component:template_name for templates and component:template_name:job_name for jobsCONFIG id DELETENETDATA_DEBUG_DYNCFG=1 environment variable when running Netdata to see detailed logs/var/lib/netdata/config//api/v3/config?id=<your-config-id>