traefik/traefik
Configuration
Configuration in Traefik is split into three packages. Knowing which package owns which type saves a lot of confusion when reading the source.
Three configuration kinds
| Kind | Package | Lifecycle | Sources |
|---|---|---|---|
| Static | pkg/config/static |
Loaded at startup, immutable thereafter | File (traefik.yml/.toml), CLI flags, env vars |
| Dynamic | pkg/config/dynamic |
Continuously refreshed at runtime | Providers (Docker, Kubernetes, file, KV stores, …) |
| Runtime | pkg/config/runtime |
In-memory mirror of dynamic + status | Built by pkg/server/router from dynamic |
Static configuration
pkg/config/static/static_config.go defines the top-level Configuration struct. Notable fields:
| Field | Type | Purpose |
|---|---|---|
Global |
*Global |
checkNewVersion, sendAnonymousUsage. |
EntryPoints |
EntryPoints |
Map of named listeners (web, websecure, traefik, …). See pkg/config/static/entrypoints.go. |
Providers |
*Providers |
Per-provider configuration blobs. |
API |
*API |
Dashboard / API enable + insecure / debug flags. |
Metrics |
*otypes.Metrics |
Prometheus / OTLP / Datadog / StatsD / InfluxDB exporter config. |
Tracing |
*Tracing |
OpenTelemetry tracer setup. |
Log, AccessLog |
*otypes.TraefikLog, *otypes.AccessLog |
Logging output, level, format. |
CertificatesResolvers |
map[string]CertificateResolver |
ACME / Tailscale resolvers. |
Experimental |
*Experimental |
Plugins, hub, kubernetes-gateway flags. |
OCSP |
*tls.OCSPConfig |
OCSP stapling. |
Spiffe |
*SpiffeClientConfig |
SPIFFE workload-identity. |
The static configuration also encodes the providers' precedence list:
var providerNames = []string{
gateway.ProviderName,
crd.ProviderName,
ingress.ProviderName,
ingressnginx.ProviderName,
docker.SwarmName,
docker.DockerName,
file.ProviderName,
redis.ProviderName,
knative.ProviderName,
consul.ProviderName,
consulcatalog.ProviderName,
nomad.ProviderName,
etcd.ProviderName,
ecs.ProviderName,
http.ProviderName,
zk.ProviderName,
rest.ProviderName,
}This is the order used when registering providers and when building the default rules. It is also the order used in error messages — handy when grepping logs.
Loading order
cmd/traefik/traefik.go sets up four loaders in this order:
loaders := []cli.ResourceLoader{
&tcli.DeprecationLoader{},
&tcli.FileLoader{},
&tcli.FlagLoader{},
&tcli.EnvLoader{},
}Later loaders override earlier ones. So CLI flags beat the file, environment variables beat CLI flags, and the deprecation loader runs first to remap old field names.
The actual decoding is done by github.com/traefik/paerser, which reflects on the struct tags. Every static-config field declares all four formats:
EntryPoints EntryPoints `description:"…" json:"entryPoints,omitempty" toml:"entryPoints,omitempty" yaml:"entryPoints,omitempty" export:"true"`Validation
After loading, the binary calls:
staticConfiguration.SetEffectiveConfiguration()
staticConfiguration.ValidateConfiguration()SetEffectiveConfiguration fills defaults and resolves cross-field implications (e.g. enabling the traefik entry point when API is enabled). ValidateConfiguration rejects logically inconsistent setups (missing entry points, non-routable rules).
Dynamic configuration
pkg/config/dynamic is where routers, services, middlewares, and TLS material live. It is split by protocol:
| File | Defines |
|---|---|
pkg/config/dynamic/http_config.go |
HTTPConfiguration, Router, Service, LoadBalancerService, WeightedRoundRobin, mirroring, … |
pkg/config/dynamic/tcp_config.go |
TCP variants of the above. |
pkg/config/dynamic/udp_config.go |
UDP variants. |
pkg/config/dynamic/middlewares.go |
All HTTP middleware configuration types (~65k bytes — every middleware lives here). |
pkg/config/dynamic/tcp_middlewares.go |
TCP middleware types. |
pkg/config/dynamic/config.go |
Configuration and Message envelopes used by providers. |
The top-level type providers emit is dynamic.Configuration, which contains HTTP, TCP, UDP, and TLS sub-configurations. Providers wrap that into dynamic.Message{ProviderName, Configuration} and push it onto a channel.
Configuration messages
type Message struct {
ProviderName string
Configuration *Configuration
}The ProviderName is required because the watcher deduplicates per provider. Two different providers can produce overlapping configurations — the watcher merges them into a single snapshot before dispatching.
Deepcopy
Configuration types implement deepcopy so callers can freely mutate snapshots. The deepcopy functions live in pkg/config/dynamic/zz_generated.deepcopy.go (78k bytes, generated by controller-gen). Don't edit by hand; run make generate-crd after changing types.
Runtime configuration
pkg/config/runtime is the dynamic configuration plus per-object status. It is the model the dashboard sees.
type Configuration struct {
Routers map[string]*RouterInfo
Middlewares map[string]*MiddlewareInfo
Services map[string]*ServiceInfo
TCPRouters map[string]*TCPRouterInfo
// ...
}
type RouterInfo struct {
*dynamic.Router
Err []string
Status string
Using []string
}Status is one of enabled, disabled, or warning. Err carries the human-readable reason. The runtime configuration is built each apply by pkg/server/router/router.go and read by pkg/api/handler.go.
The configuration watcher
pkg/server/configurationwatcher.go ties providers to the router factory.
type ConfigurationWatcher struct {
providerAggregator provider.Provider
defaultEntryPoints []string
allProvidersConfigs chan dynamic.Message
newConfigs chan dynamic.Configurations
requiredProvider string
configurationListeners []func(dynamic.Configuration)
configurationTransformers []func(context.Context, dynamic.Configurations) dynamic.Configurations
routinesPool *safe.Pool
}Three goroutines run inside Start:
startProviderAggregator— callsproviderAggregator.Provide(c.allProvidersConfigs, c.routinesPool)to fan in messages from every configured provider.receiveConfigurations— reads fromallProvidersConfigs, deduplicates per provider, applies transformers, and sends the merged snapshot tonewConfigsnon-blockingly.applyConfigurations— reads the latest snapshot fromnewConfigsand calls each registered listener.
The non-blocking send into newConfigs is what guarantees that if a burst of provider messages arrives, only the latest one is applied. Older snapshots in flight get dropped, never queued.
Listeners
Listeners are registered via AddListener. Two are always present:
- The router factory listener (
pkg/server/routerfactory.go), which rebuilds the live router tree. - The TLS manager listener (
pkg/tls/tlsmanager.go), which updates the certificate store.
Plugins that ship with Traefik (e.g. Hub) attach themselves as transformers — they get a chance to mutate the configuration before listeners see it.
Provider aggregator
pkg/provider/aggregator/aggregator.go is a provider.Provider that fans out across multiple inner providers. It owns:
internalProvider— the implicittraefikprovider that injects required routers (e.g. for the dashboard).fileProvider— the file/directory provider, present even if the user didn't configure it (it backs thedefaultRuleinfrastructure for some integrations).- All user-configured providers from the static config.
- Optional Hub-related providers wrapped at the edges.
Provide runs each inner provider in its own goroutine via the routinesPool. Each inner provider's messages get tagged with its ProviderName before flowing into the aggregator's output channel. This is what gives the watcher the per-provider deduplication key.
Practical implications
- To add a configuration field: edit the matching struct in
pkg/config/staticorpkg/config/dynamic, add tags for every format, runmake generate generate-crd. - To change configuration application semantics: it's almost always the watcher loop in
configurationwatcher.go. Mind the channel ordering and the listener contract. - To debug "why isn't my change applying?": enable
--log.level=DEBUGand look for "Skipping unchanged configuration" or listener-side errors.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.