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 plugin
  • AsStringSlice(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:

  1. Parses the TOML file into an AST
  2. Walks plugin sections and checks for entries in migrations.PluginMigrations
  3. Executes matching migrations, replacing old blocks with transformed TOML
  4. 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.PluginMigrations automatically when loading configuration files containing legacy plugin sections.
  • Registry architecture: The migrations/registry.go file manages all migration functions via AddPluginMigration, while migrations/utils.go provides CreateTOMLStruct for building modern TOML structures.
  • Function signature: Migrations accept an *ast.Table, return transformed TOML bytes, warning messages, and errors.
  • CLI workflow: Use --test to preview migrations and --config-output to write transformed configurations to disk.
  • Concrete examples: The inputs_udp_listener migration demonstrates transforming udp_buffer_size to read_buffer_size and prepending udp:// 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →