# How to Configure Dynamic Configuration Sources in Kratos: File, Etcd, Consul, and Apollo

> Learn to configure dynamic configuration sources like file, etcd, consul, and Apollo in Kratos. Hot-reload your application settings without restarts using Kratos's config.Source interface and Watch method.

- Repository: [Kratos/kratos](https://github.com/go-kratos/kratos)
- Tags: how-to-guide
- Published: 2026-03-02

---

**Kratos unifies dynamic configuration through the `config.Source` interface, which continuously monitors external stores via the `Watch()` method and hot-reloads values into your application without restarts.**

The go-kratos/kratos microservice framework provides a pluggable configuration subsystem that supports real-time updates from multiple backends. By leveraging the `config.Source` abstraction defined in [`config/source.go`](https://github.com/go-kratos/kratos/blob/main/config/source.go), developers can configure dynamic configuration sources in Kratos to load settings from local files, distributed key-value stores, and remote configuration centers while maintaining zero-downtime configuration updates.

## Understanding the Kratos Configuration Architecture

### The Source Interface

At the heart of dynamic configuration lies the `config.Source` interface located in [[`config/source.go`](https://github.com/go-kratos/kratos/blob/main/config/source.go)](https://github.com/go-kratos/kratos/blob/main/config/source.go). Every backend must implement two critical methods:

- **`Load()`** – Retrieves the current set of key/value pairs from the external store.
- **`Watch()`** – Returns a `config.Watcher` that yields new `*config.KeyValue` slices whenever the underlying data changes.

This abstraction allows the core framework to treat file watchers, Etcd listeners, Consul blocking queries, and Apollo notifications identically.

### The Config Lifecycle

When you initialize the system using `config.New` (defined in [[`config/config.go`](https://github.com/go-kratos/kratos/blob/main/config/config.go)](https://github.com/go-kratos/kratos/blob/main/config/config.go)), you supply sources via the `WithSource` functional option from [[`config/options.go`](https://github.com/go-kratos/kratos/blob/main/config/options.go)](https://github.com/go-kratos/kratos/blob/main/config/options.go). The lifecycle follows this sequence:

1. **Initial Load** – Each source's `Load()` method executes once, merging key/value pairs into an internal cache.
2. **Watcher Initialization** – A dedicated goroutine runs `config.watch` for every source, blocking on the `Watch()` stream.
3. **Hot Reload** – When a watcher detects changes, the framework merges updates, resolves placeholder variables (e.g., `${ENV_VAR}`), and notifies registered observers.

Because watchers run continuously, any modification in the external store—whether a file edit, Etcd key update, Consul KV change, or Apollo namespace refresh—automatically propagates to your running service.

## Configuring File-Based Dynamic Configuration

The file source in [[`contrib/config/file/file.go`](https://github.com/go-kratos/kratos/blob/main/contrib/config/file/file.go)](https://github.com/go-kratos/kratos/blob/main/contrib/config/file/file.go) monitors directories or individual files for changes. It automatically detects formats (JSON, YAML, XML, TOML) from file extensions and uses the appropriate codec from the `encoding` package.

```go
import (
    "github.com/go-kratos/kratos/v2"
    "github.com/go-kratos/kratos/v2/config"
    "github.com/go-kratos/kratos/v2/config/file"
    "github.com/go-kratos/kratos/v2/log"
)

func main() {
    // Create a file source (can be a directory or a single file)
    src := file.NewSource("./configs")

    // Build the config subsystem
    cfg := config.New(
        config.WithSource(src),                // dynamic source
        config.WithResolveActualTypes(true),   // optional type conversion
    )
    if err := cfg.Load(); err != nil {
        log.Fatalf("load config error: %v", err)
    }

    // Access a value
    port := cfg.Value("http.port").String()
    log.Infof("http port = %s", port)

    // React to future changes of the key
    _ = cfg.Watch("http.port", func(k string, v config.Value) {
        log.Infof("port changed to %s", v.String())
    })

    // Start your Kratos application with the config as a dependency
    app := kratos.New(
        kratos.Config(cfg),
        // other options …
    )
    if err := app.Run(); err != nil {
        log.Fatalf("app run error: %v", err)
    }
}

```

## Configuring Etcd as a Dynamic Source

For distributed systems, the Etcd source in [[`contrib/config/etcd/config.go`](https://github.com/go-kratos/kratos/blob/main/contrib/config/etcd/config.go)](https://github.com/go-kratos/kratos/blob/main/contrib/config/etcd/config.go) watches a key prefix and streams `Watch` events from the Etcd client.

```go
import (
    "github.com/go-kratos/kratos/v2/config"
    etcdconf "github.com/go-kratos/kratos/contrib/config/etcd/v2"
    clientv3 "go.etcd.io/etcd/client/v3"
)

func etcdConfig() config.Config {
    // Etcd client (assume endpoint is reachable)
    cli, _ := clientv3.New(clientv3.Config{
        Endpoints: []string{"http://127.0.0.1:2379"},
    })
    src, _ := etcdconf.New(cli,
        etcdconf.WithPath("/myapp/config"),
        etcdconf.WithPrefix(true), // watch all keys under the prefix
    )
    return config.New(config.WithSource(src))
}

```

The `WithPrefix(true)` option ensures that any key under `/myapp/config` triggers an update, enabling hierarchical configuration trees.

## Configuring Consul KV Store

The Consul integration in [[`contrib/config/consul/config.go`](https://github.com/go-kratos/kratos/blob/main/contrib/config/consul/config.go)](https://github.com/go-kratos/kratos/blob/main/contrib/config/consul/config.go) uses blocking queries to efficiently watch for changes in the HashiCorp Consul KV store.

```go
import (
    "github.com/go-kratos/kratos/v2/config"
    consulconf "github.com/go-kratos/kratos/contrib/config/consul/v2"
    "github.com/hashicorp/consul/api"
)

func consulConfig() config.Config {
    client, _ := api.NewClient(api.DefaultConfig())
    src, _ := consulconf.New(client, consulconf.WithPath("myapp/config"))
    return config.New(config.WithSource(src))
}

```

This source polls Consul using the `ModifyIndex` mechanism, ensuring you receive updates immediately when an operator changes a value in the UI or API.

## Configuring Apollo Configuration Center

For Apollo users, the source in [[`contrib/config/apollo/apollo.go`](https://github.com/go-kratos/kratos/blob/main/contrib/config/apollo/apollo.go)](https://github.com/go-kratos/kratos/blob/main/contrib/config/apollo/apollo.go) supports multiple namespaces, secret-based authentication, and local backup caching.

```go
import (
    "github.com/go-kratos/kratos/v2/config"
    apolloconf "github.com/go-kratos/kratos/contrib/config/apollo/v2"
)

func apolloConfig() config.Config {
    src := apolloconf.NewSource(
        apolloconf.WithAppID("demo-app"),
        apolloconf.WithCluster("default"),
        apolloconf.WithEndpoint("http://apollo.meta.server"),
        apolloconf.WithNamespace("application,extra.yaml"),
        apolloconf.WithSecret("my-secret"),
        apolloconf.WithEnableBackup(),
    )
    return config.New(config.WithSource(src))
}

```

The `WithNamespace` option accepts comma-separated values, allowing you to compose configuration from multiple Apollo namespaces simultaneously.

## Watching Configuration Changes at Runtime

Beyond automatic reloading, you can register granular observers for specific keys using the `Watch` method. When the internal `config.watch` goroutine detects a diff (implemented around line 84 in [`config/config.go`](https://github.com/go-kratos/kratos/blob/main/config/config.go)), it invokes your callback with the new `config.Value`.

```go
_ = cfg.Watch("database.max_connections", func(key string, value config.Value) {
    // Convert to int and apply to connection pool
    maxConn, _ := value.Int()
    pool.SetMaxOpenConns(maxConn)
})

```

This pattern enables reactive programming—your service adjusts thread pools, feature flags, or logging levels instantly when operators update the remote store.

## Summary

- **Unified Interface** – All dynamic sources implement `config.Source` with `Load()` and `Watch()` methods defined in [`config/source.go`](https://github.com/go-kratos/kratos/blob/main/config/source.go).
- **Lifecycle Management** – `config.New` accepts `WithSource` options, loads initial data via `config.Load`, and spawns background watchers via `config.watch` in [`config/config.go`](https://github.com/go-kratos/kratos/blob/main/config/config.go).
- **Backend Variety** – File, Etcd, Consul, and Apollo sources live in `contrib/config/` and share identical integration patterns.
- **Hot Reloading** – Changes propagate automatically through the watcher channel, merging updates and resolving placeholders without process restarts.
- **Observable Updates** – Register key-specific callbacks using `cfg.Watch` to react to precise configuration mutations at runtime.

## Frequently Asked Questions

### How does Kratos hot-reload configuration without restarting the service?

Kratos spawns a background goroutine for each source that blocks on the `Watch()` method. When the underlying backend detects a change (file modification, Etcd event, Consul index change, or Apollo notification), the watcher pushes new key/value pairs to the core `config.Config` instance. The framework merges these updates into the internal cache and resolves any variable placeholders, making new values available immediately to `cfg.Value()` calls and registered observers.

### Can I combine multiple dynamic sources like Etcd and file in the same application?

Yes. The `WithSource` option in [`config/options.go`](https://github.com/go-kratos/kratos/blob/main/config/options.go) accepts variadic arguments, allowing you to pass multiple sources to `config.New`. For example, you can load baseline settings from a local file and override specific keys with values from Etcd. The sources are loaded in order and merged, with later sources taking precedence in case of key collisions.

### What configuration formats are supported by the Kratos file source?

The file source in [`contrib/config/file/file.go`](https://github.com/go-kratos/kratos/blob/main/contrib/config/file/file.go) automatically detects the format from file extensions and uses the corresponding codec from the `encoding` subsystem. Supported formats include **JSON**, **YAML**, **XML**, **TOML**, and **YML**. You can place multiple format files in the same directory; the source loads and merges all valid configuration files it discovers.

### How do I watch a specific configuration key for changes?

Use the `Watch` method on your `config.Config` instance, passing the key path and a callback function. When the internal diff detection logic in [`config/config.go`](https://github.com/go-kratos/kratos/blob/main/config/config.go) determines that the value for your key has changed, it invokes your callback with the key name and new `config.Value`. This is ideal for adjusting connection limits, toggling feature flags, or reloading TLS certificates without restarting the application.