Open-Source Wikis

/

Traefik

/

How to contribute

/

Patterns and conventions

traefik/traefik

Patterns and conventions

Cross-cutting conventions used throughout the Go code. Mirroring these makes review faster.

Configuration types

Almost every public type with json, yaml, toml tags is part of the configuration surface. Two rules dominate:

  1. Every field has tags for every supported source format. Look at any struct under pkg/config/static or pkg/config/dynamic — every field carries json:"…,omitempty", yaml:"…,omitempty", toml:"…,omitempty", description:"…", and often export:"true" or label:"allowEmpty".

    EntryPoints EntryPoints `description:"Entry points definition." json:"entryPoints,omitempty" toml:"entryPoints,omitempty" yaml:"entryPoints,omitempty" export:"true"`
  2. description: is documentation. It is used to generate the CLI help, the env-var help, and the public documentation under docs/content/reference/. Don't write throwaway descriptions — they ship.

When you add a field, also:

  • Provide a sensible zero-value default (SetDefaults method, see pkg/config/dynamic/middlewares.go).
  • Run make generate to refresh the documentation reference.
  • Run make generate-crd if the type is part of a CRD schema.

Logging

The logger of choice is github.com/rs/zerolog. Conventions:

  • Get a context-scoped logger with log := log.Ctx(ctx) (or use logs.Logger(ctx) from pkg/observability/logs).
  • Build structured fields with .Str("router", name).Str("entryPoint", ep).
  • Use the Msgf form only when you need a format string; prefer .Str fields so logs are queryable.
  • Reserve Error level for conditions that need attention; Warn for recoverable misconfiguration; Info for lifecycle events; Debug for the configuration loop and per-request hooks.

Fmt'd errors are wrapped with fmt.Errorf("...: %w", err) and logged once at the boundary that decides what to do with them.

Errors

Three patterns recur:

  • Provider/middleware constructors return (handler, error). The error includes context (fmt.Errorf("creating compress middleware: %w", err)).
  • Lifecycle errors propagate upward from Server.Start to the top of runCmd.
  • Per-request errors at HTTP boundaries are wrapped in helpers (pkg/middlewares/recovery, pkg/middlewares/customerrors).

Goroutines

Goroutines are launched through pkg/safe, not directly. Two helpers:

  • safe.Go(func() {...}) for fire-and-forget goroutines that should not panic the process.
  • safe.Pool for context-aware goroutines whose lifetime is tied to the server. The pool is the one parameter handed into virtually every starting function (Server.Start, ConfigurationWatcher.Start, provider Provide).

Anything that listens forever or polls externally goes through a safe.Pool. This is what allows a single srv.routinesPool.Stop() to take the process down cleanly.

Concurrency state

  • Per-provider configuration messages flow over channels. dynamic.Message is the unit of exchange.
  • The "currently active" router tree is swapped atomically by entry points (pkg/server/server_entrypoint_tcp.go uses *atomic.Value-style swapping under the covers; the public surface is SwitchRouter).
  • Read-mostly state (TLS certificate store, runtime configuration) is guarded by sync.RWMutex (pkg/tls/tlsmanager.go, pkg/config/runtime).

Naming

  • Files are lowercase, words run together: routerfactory.go, configurationwatcher.go, handler_http.go. Avoid kebab-case in Go file names.
  • Exported types use the noun form of the concept: Router, Service, Middleware, EntryPoint, Provider, Configuration.
  • Internal helpers stay lowercase. Tests live next to the code (foo.go / foo_test.go).

Provider conventions

A new provider implements the small interface in pkg/provider/provider.go:

type Provider interface {
    Init() error
    Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error
}

Per-provider conventions seen across pkg/provider/*:

  • Each provider has a Name constant (e.g. docker.DockerName, crd.ProviderName).
  • Each provider has its own logger context: log.With().Str("providerName", Name).Logger().WithContext(ctx).
  • Polling providers use backoff from cenkalti/backoff/v4 to retry transient errors.
  • Watch-style providers (Kubernetes, Docker events, Consul watch) reconnect indefinitely on failure.

Middleware conventions

Each middleware lives in its own subpackage of pkg/middlewares/<name>. The standard shape:

type fooMiddleware struct {
    next http.Handler
    name string
    // …config…
}

func New(ctx context.Context, next http.Handler, cfg dynamic.Foo, name string) (http.Handler, error) {
    // validate cfg
    return &fooMiddleware{next: next, name: name, /* … */}, nil
}

func (m *fooMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { /* … */ }

func (m *fooMiddleware) GetTracingInformation() (string, string, trace.SpanKind) {
    return m.name, typeName, trace.SpanKindInternal
}

The GetTracingInformation method is consumed by pkg/middlewares/observability to label spans. The middleware factory is registered with pkg/server/middleware/middlewares.go.

Deprecation

Use // Deprecated: doc comments on fields and methods. Example, from pkg/config/static/static_config.go:

// Core configures Traefik core behavior.
type Core struct {
    // Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax.
    DefaultRuleSyntax string `description:"…"`
}

Deprecated fields keep working until the next major version. The tcli.DeprecationLoader (pkg/cli) emits a warning at startup when a deprecated field is set.

Public API stability

There is no public Go API to honor — Traefik is a binary. Tests under pkg/.../*_test.go are part of the codebase, not part of an API contract. Refactor freely as long as the configuration surface (and its generated docs) stays stable.

What gets shipped

The Go binary embeds:

  • The dashboard (webui/static, via webui/embed.go).
  • The error page templates (pkg/middlewares/customerrors).
  • Generated JSON schemas for CRDs and Gateway API conformance fixtures.

Everything else — configuration files, plugins, certificates — is loaded from disk or providers at runtime.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Patterns and conventions – Traefik wiki | Factory