Writing Config Adapters

For various reasons, you may wish to configure Caddy using a format that is not JSON. Caddy has first-class support for this through config adapters.

If one does not already exist for the language/syntax/format you prefer, you can write one!

Template

Here’s a template you can start with:

  1. package myadapter
  2. import (
  3. "fmt"
  4. "github.com/caddyserver/caddy/v2/caddyconfig"
  5. )
  6. func init() {
  7. caddyconfig.RegisterAdapter("adapter_name", MyAdapter{})
  8. }
  9. // MyAdapter adapts ____ to Caddy JSON.
  10. type MyAdapter struct{
  11. }
  12. // Adapt adapts the body to Caddy JSON.
  13. func (a MyAdapter) Adapt(body []byte, options map[string]interface{}) ([]byte, []caddyconfig.Warning, error) {
  14. // TODO: parse body and convert it to JSON
  15. return nil, nil, fmt.Errorf("not implemented")
  16. }

The returned JSON should not be indented; it should always be compact. The caller can always prettify it if they want to.

Note that while config adapters are Caddy plugins, they are not Caddy modules because they do not integrate into a part of the config (but they will show up in list-modules for convenience). Thus, they do not have Provision() or Validate() methods or follow the rest of the module lifecycle. They need only implement the Adapter interface and be registered as adapters.

When populating fields of the config that are json.RawMessage types (i.e. module fields), use the JSON() and JSONModuleObject() functions:

  • caddyconfig.JSON() is for marshaling module values without the module name embedded. (Often used for ModuleMap fields where the module name is the map key.)
  • caddyconfig.JSONModuleObject() is for marshaling module values with the module name added to the object. (Used pretty much everywhere else.)

Caddyfile Server Types

It is also possible to implement a custom Caddyfile format. The Caddyfile adapter is a single adapter implementation and its default “server type” is HTTP, but it supports alternate “server types” at registration. For example, the HTTP Caddyfile is registered like so:

  1. func init() {
  2. caddyconfig.RegisterAdapter("caddyfile", caddyfile.Adapter{ServerType: ServerType{}})
  3. }

You would implement the caddyfile.ServerType interface and register your own adapter accordingly.