# How Caddy's Config Adapter System Works: Converting Custom Formats to JSON

> Discover how Caddy's config adapter system converts custom formats like the Caddyfile into its native JSON structure via a Go interface and global registry.

- Repository: [Caddy/caddy](https://github.com/caddyserver/caddy)
- Tags: internals
- Published: 2026-03-03

---

**Caddy's config adapter system transforms arbitrary configuration formats—such as the Caddyfile or custom domain-specific languages—into Caddy's native JSON structure through a standardized Go interface and global registry.**

The `caddyserver/caddy` repository implements a pluggable adapter architecture that decouples configuration syntax from the core server logic. This system allows users to write configs in the **Caddyfile** format or any custom format, while the server internally operates on a unified JSON representation.

## The Adapter Interface and Core Concepts

At the heart of Caddy's config adapter system lies a simple Go interface defined in [`caddyconfig/configadapters.go`](https://github.com/caddyserver/caddy/blob/main/caddyconfig/configadapters.go). Any type implementing this interface can serve as a configuration adapter.

The **Adapter interface** requires a single method:

```go
Adapt(body []byte, options map[string]any) ([]byte, []caddyconfig.Warning, error)

```

This method receives the raw configuration file contents as `body`, along with an options map for additional parameters such as file paths or CLI flags. It returns three values: the adapted JSON configuration as bytes, a slice of **Warning** structs for non-fatal issues, and an error if adaptation fails.

The **Warning** struct surfaces contextual issues without halting execution:

```go
type Warning struct {
    File       string
    Line       int
    Directive  string
    Message    string
}

```

## The Adapter Registration and Lookup Mechanism

Caddy maintains a **global registry** to manage available adapters. This registry, defined as `configAdapters map[string]Adapter` in [`caddyconfig/configadapters.go`](https://github.com/caddyserver/caddy/blob/main/caddyconfig/configadapters.go), maps string names to adapter implementations.

### Registering Adapters

Packages implement adapters and register them during initialization using the `RegisterAdapter` function:

```go
func RegisterAdapter(name string, adapter Adapter) {
    if configAdapters == nil {
        configAdapters = make(map[string]Adapter)
    }
    configAdapters[name] = adapter
    // Also registers as a Caddy module for introspection
    RegisterModule(adapterModule{name: name, adapter: adapter})
}

```

This function stores the adapter in the global map and simultaneously registers it as a **Caddy module** via `adapterModule`. This dual registration allows the adapter to appear in Caddy's module list and support hot-reloading capabilities.

### Retrieving Adapters

When Caddy loads a configuration, it calls `GetAdapter(name string)` to retrieve the appropriate adapter from the registry:

```go
func GetAdapter(name string) Adapter {
    return configAdapters[name]
}

```

The core loader uses this function when processing the `--adapter` CLI flag or when auto-detecting the configuration format based on file extensions.

## How the Caddyfile Adapter Works: A Concrete Implementation

The **Caddyfile** adapter serves as the primary reference implementation for Caddy's config adapter system. Located in [`caddyconfig/caddyfile/adapter.go`](https://github.com/caddyserver/caddy/blob/main/caddyconfig/caddyfile/adapter.go), this adapter converts the Caddyfile syntax into JSON configuration.

The adapter registers itself in [`caddyconfig/httpcaddyfile/httptype.go`](https://github.com/caddyserver/caddy/blob/main/caddyconfig/httpcaddyfile/httptype.go):

```go
func init() {
    caddyconfig.RegisterAdapter("caddyfile", caddyfile.Adapter{})
}

```

### The Adaptation Flow

When `caddyfile.Adapter.Adapt` executes, it follows a three-phase process:

1. **Parsing**: The `Parse` function tokenizes the Caddyfile contents into server blocks and directives, creating an abstract syntax tree.

2. **Setup**: The `ServerType.Setup` method processes these blocks, constructing HTTP server configurations, TLS settings, and other app-specific structures. This phase handles complex logic such as global options, named routes, and matcher definitions.

3. **Marshaling**: The resulting `*caddy.Config` structure is serialized to JSON using `json.Marshal`, producing the final configuration bytes that Caddy's core consumes.

This implementation demonstrates how the abstract `Adapter` interface translates domain-specific syntax into Caddy's internal JSON representation.

## Creating a Custom Config Adapter

Developers can extend Caddy's config adapter system by implementing the `Adapter` interface and registering the implementation during package initialization.

### Implementation Structure

A custom adapter requires a type that implements the `Adapt` method:

```go
package myadapter

import (
    "encoding/json"
    "fmt"
    
    "github.com/caddyserver/caddy/v2"
    "github.com/caddyserver/caddy/v2/caddyconfig"
)

type MyAdapter struct{}

func (a MyAdapter) Adapt(body []byte, opts map[string]any) ([]byte, []caddyconfig.Warning, error) {
    // Example: Validate that input is already valid JSON
    var cfg caddy.Config
    if err := json.Unmarshal(body, &cfg); err != nil {
        return nil, nil, fmt.Errorf("invalid JSON: %w", err)
    }
    
    // Re-marshal to ensure consistent formatting
    jsonBytes, err := json.Marshal(cfg)
    if err != nil {
        return nil, nil, err
    }
    
    return jsonBytes, nil, nil
}

func init() {
    caddyconfig.RegisterAdapter("myformat", MyAdapter{})
}

```

### Usage

Once compiled into a Caddy build, users invoke the custom adapter via CLI:

```bash
caddy run --config config.myfmt --adapter myformat

```

Caddy retrieves `"myformat"` using `GetAdapter`, invokes `Adapt`, and loads the resulting JSON configuration.

## Summary

Caddy's config adapter system provides a clean abstraction for converting arbitrary configuration formats into the server's native JSON structure:

- **Standardized Interface**: The `Adapter` interface in [`caddyconfig/configadapters.go`](https://github.com/caddyserver/caddy/blob/main/caddyconfig/configadapters.go) defines the contract for configuration conversion through the `Adapt` method.
- **Global Registry**: The `configAdapters` map and `RegisterAdapter` function enable dynamic discovery of adapters, while `GetAdapter` facilitates runtime retrieval.
- **Reference Implementation**: The Caddyfile adapter demonstrates the full adaptation pipeline: parsing tokens, setting up server configurations, and marshaling to JSON.
- **Extensibility**: Third-party developers implement the `Adapter` interface and register implementations during `init()` to add support for custom configuration languages.

## Frequently Asked Questions

### What is the config adapter system in Caddy?

The config adapter system is a pluggable architecture that converts configuration files from various formats—such as the Caddyfile or custom domain-specific languages—into Caddy's native JSON configuration. It consists of a Go interface definition, a global registry for adapter lookup, and concrete implementations that handle the actual parsing and transformation logic.

### How do I use a custom config adapter with Caddy?

To use a custom adapter, implement the `caddyconfig.Adapter` interface with an `Adapt` method that transforms your format into valid Caddy JSON, then register it using `caddyconfig.RegisterAdapter` in your package's `init` function. After compiling your code into the Caddy binary, specify your adapter via the `--adapter` flag when running `caddy run` or `caddy start`.

### What is the difference between the Caddyfile and JSON configuration formats?

The Caddyfile is a human-friendly, indentation-based configuration syntax that gets converted to JSON through the built-in Caddyfile adapter, while JSON is Caddy's native internal format that provides explicit structure and full access to all configuration options. The Caddyfile adapter handles the transformation by parsing the file into tokens, setting up server blocks and directives, and marshaling the result to JSON.

### Where is the config adapter interface defined in the Caddy source code?

The `Adapter` interface is defined in [`caddyconfig/configadapters.go`](https://github.com/caddyserver/caddy/blob/main/caddyconfig/configadapters.go) within the `github.com/caddyserver/caddy/v2` repository. This file also contains the `Warning` struct, the global `configAdapters` registry map, and the `RegisterAdapter` and `GetAdapter` functions that manage adapter lifecycle and discovery.