How to Migrate from Legacy Plugin Configurations Using Telegraf Migrations
Telegraf automatically upgrades legacy plugin configurations at startup by running registered migration functions that rewrite deprecated TOML blocks into modern formats while logging conversion warnings.
The influxdata/telegraf repository includes a built-in migrations package that systematically handles breaking configuration changes, allowing you to migrate from legacy plugin configurations using Telegraf migrations without manual file editing. When Telegraf parses your TOML configuration, it detects deprecated plugin sections and executes registered migration functions to transform them into current formats before the agent initializes.
How the Telegraf Migration System Works
The migration architecture consists of a central registry, utility functions, and plugin-specific migration files that cooperate to transform configuration ASTs (Abstract Syntax Trees).
Migration Registry in registry.go
At the heart of the system is migrations/registry.go, which maintains four global registries: PluginMigrations, PluginOptionMigrations, GeneralMigrations, and GlobalMigrations. Plugins register their migration functions using AddPluginMigration:
// In migrations/registry.go
func AddPluginMigration(name string, f PluginMigrationFunc) {
if _, found := PluginMigrations[name]; found {
panic(fmt.Errorf("plugin migration function already registered for %q", name))
}
PluginMigrations[name] = f
}
Each legacy plugin calls this registration in its init() function. For example, the deprecated UDP listener plugin registers its migrator as follows:
func init() {
migrations.AddPluginMigration("inputs.udp_listener", migrate)
}
Transformation Utilities in utils.go
The migrations/utils.go file provides helper functions for constructing new TOML structures. The most important utility is CreateTOMLStruct, which generates the scaffold for the modern plugin configuration:
CreateTOMLStruct(pluginType, pluginName string)– Initializes a new configuration structure for the target pluginAsStringSlice(interface{})– Safely converts legacy array formats to string slices
Plugin-Specific Migration Files
Individual migrations live in dedicated files such as migrations/inputs_udp_listener/migration.go. These files encode the specific translation logic for each deprecated plugin, handling field renames, value transformations, and deprecated option removal.
Anatomy of a Migration Function
A migration function conforms to the signature func(*ast.Table) ([]byte, string, error) and executes a four-step transformation process. Here is the actual implementation pattern used in the UDP listener migration:
func migrate(tbl *ast.Table) ([]byte, string, error) {
// 1. Decode the legacy TOML table
var old udpListener
if err := toml.UnmarshalTable(tbl, &old); err != nil {
return nil, "", err
}
// 2. Transform legacy fields to modern equivalents
plugin := make(map[string]interface{})
var msg string
for k, v := range old {
switch k {
case "service_address":
// Prepend protocol scheme to bare addresses
plugin["service_address"] = "udp://" + v.(string)
case "allowed_pending_messages":
// Log deprecation warning for removed options
msg += "allowed_pending_messages is deprecated and will be ignored\n"
case "udp_buffer_size":
// Rename legacy fields to current names
plugin["read_buffer_size"] = v
default:
plugin[k] = v
}
}
// 3. Build new TOML structure using the utility
cfg := migrations.CreateTOMLStruct("inputs", "socket_listener")
cfg.Add("inputs", "socket_listener", plugin)
// 4. Marshal and return new configuration
buf, err := toml.Marshal(cfg)
if err != nil {
return nil, "", err
}
return buf, msg, nil
}
The function returns three values: the new TOML configuration bytes, a concatenated warning string for deprecated options, and any fatal errors encountered during parsing.
Running Configuration Migrations
You do not invoke migration functions manually. Telegraf executes them automatically during the configuration loading phase.
Automatic Migration at Startup
When you start Telegraf with a legacy configuration, the loader:
- Parses the TOML file into an AST
- Walks plugin sections and checks for entries in
migrations.PluginMigrations - Executes matching migrations, replacing old blocks with transformed TOML
- Logs warning messages returned by the migration functions
# Start Telegraf with a legacy configuration file
telegraf --config legacy.conf --test
The --test flag performs a dry-run, showing migration warnings without sending metrics:
WARNING: The deprecated 'udp_buffer_size' setting will be dropped; use 'read_buffer_size'
WARNING: Legacy parameter 'allowed_pending_messages' is no longer supported
Generating Migrated Configuration Files
To persist the transformed configuration to disk, use the --config-output flag (available in recent Telegraf releases):
telegraf --config legacy.conf --config-output migrated.conf
Before and After Configuration
Legacy configuration fragment (pre-migration):
[inputs.udp_listener]
service_address = "127.0.0.1:8094"
allowed_pending_messages = 200
udp_buffer_size = 65535
Modern configuration (produced automatically by the migration system):
[inputs.socket_listener]
service_address = "udp://127.0.0.1:8094"
read_buffer_size = 65535
Programmatic Migration in Go Applications
If you embed Telegraf as a library, you can invoke migrations programmatically using the registry:
import (
"github.com/influxdata/telegraf/migrations"
"github.com/influxdata/toml/ast"
"log"
)
func migrateConfig(tbl *ast.Table) {
// Look up migration by legacy plugin name
migrateFunc, exists := migrations.PluginMigrations["inputs.udp_listener"]
if !exists {
log.Fatal("no migration found for plugin")
}
// Execute migration
newConfig, warningMsg, err := migrateFunc(tbl)
if err != nil {
log.Fatalf("migration failed: %v", err)
}
if warningMsg != "" {
log.Printf("migration warnings: %s", warningMsg)
}
// newConfig contains []byte of updated TOML
}
This pattern allows external tools to validate or batch-process legacy Telegraf configurations using the same logic as the main agent.
Summary
- Automatic execution: Telegraf runs registered migrations from
migrations.PluginMigrationsautomatically when loading configuration files containing legacy plugin sections. - Registry architecture: The
migrations/registry.gofile manages all migration functions viaAddPluginMigration, whilemigrations/utils.goprovidesCreateTOMLStructfor building modern TOML structures. - Function signature: Migrations accept an
*ast.Table, return transformed TOML bytes, warning messages, and errors. - CLI workflow: Use
--testto preview migrations and--config-outputto write transformed configurations to disk. - Concrete examples: The
inputs_udp_listenermigration demonstrates transformingudp_buffer_sizetoread_buffer_sizeand prependingudp://to service addresses.
Frequently Asked Questions
How do I know if my Telegraf configuration contains legacy plugins?
Telegraf logs migration warnings at startup when it detects deprecated plugin sections. Run telegraf --config your-file.conf --test to see if any migration messages appear. If warnings reference deprecated options or automatic conversions, your configuration requires migration.
Can I disable automatic migrations in Telegraf?
No, the migration system is mandatory and runs during the configuration parsing phase. However, migrations only modify the in-memory representation of your configuration unless you explicitly write the output using --config-output. The system is designed to be backwards-compatible and will not alter your original configuration file on disk.
What happens if a migration fails?
If a migration returns an error, Telegraf halts startup with a fatal error message indicating which plugin migration failed and why. If the migration succeeds but returns warning messages (indicating deprecated options were dropped or modified), Telegraf starts normally but logs the warnings to stderr so you can review the changes.
Where can I find the migration logic for a specific deprecated plugin?
Migration implementations reside in migrations/<plugin_name>/migration.go within the Telegraf repository. For example, the UDP listener migration is located at migrations/inputs_udp_listener/migration.go. You can also search for AddPluginMigration calls in the plugins/ directory to find migrations registered by output or input plugins like plugins/outputs/sql/sql.go or plugins/outputs/kafka/kafka.go.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →