Skip to content

Caddyfile changes with every polling interval #707

@magnetic-domelike-twisting

Description

For the past couple months that I've been using caddy-docker-proxy it's been fantastic, this is a great project. I'd notice certain things, particularly applications utilizing websockets had issues, seemingly random disconnects. I finally put some time into troubleshooting this and discovered that every 30-seconds caddy-docker-proxy was reloading the config with a new Caddyfile. This corresponded to the default polling interval for checks in changes of the Caddyfile. When checking the different Caddyfiles from one 30-second interval to the next I noticed this.

Caddyfile A

test.example.com {
	import common_config
	reverse_proxy 192.168.131.2:80 192.168.130.2:80
}

Caddyfile B (30-seconds later)

test.example.com {
	import common_config
	reverse_proxy 192.168.130.2:80 192.168.131.2:80
}

The reverse_proxy directive contains the same upstream targets, they're just sorted differently. Seems to only happen on my containers that are attached to more than one docker network. This looks like it's enough of a change that caddy-docker-file would consider it to be a new Caddyfile and reload the server, and I believe that's what was breaking my websocket applications. Anyway, Claude 3.7 helped me out and I believe this can be fixed by sorting the targets in go.cmd before they get put into the generated Caddyfile. I forked the repo, implemented a quick sort of the targets, rebuild caddy with my fork, and I haven't seen a single unexpected reload since.

Not sure if this is worth integrating, but I figured it'd be worth posting here. The fix is to just sort the targets in labels.go before they get transformed, not sure what else this might impact, but it appears to have solved this for me. I just imported sort and sorted the targets on line 18 before they get transformed.

labels.go

package generator

import (
        "strconv"
        "strings"
        "text/template"
        "sort"

        "github.com/lucaslorentz/caddy-docker-proxy/v2/caddyfile"
)

type targetsProvider func() ([]string, error)

func labelsToCaddyfile(labels map[string]string, templateData interface{}, getTargets targetsProvider) (*caddyfile.Container, error) {
        funcMap := template.FuncMap{
                "upstreams": func(options ...interface{}) (string, error) {
                        targets, err := getTargets()
                        sort.Strings(targets)
                        transformed := []string{}
                        for _, target := range targets {
                                for _, param := range options {
                                        if protocol, isProtocol := param.(string); isProtocol {
                                                target = protocol + "://" + target
                                        } else if port, isPort := param.(int); isPort {
                                                target = target + ":" + strconv.Itoa(port)
                                        }   
                                }   
                                transformed = append(transformed, target)
                        }   
                        sort.Strings(transformed)
                        return strings.Join(transformed, " "), err 
                },  
                "http": func() string {
                        return "http"
                },  
                "https": func() string {
                        return "https"
                },  
                "h2c": func() string {
                        return "h2c"
                },  
        }   

        return caddyfile.FromLabels(labels, templateData, funcMap)
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions