fix: gate extra EndpointsConfig behind API >= 1.44

Before API 1.44 (Docker Engine 25.0), ContainerCreate only accepted a
single EndpointsConfig entry. Passing multiple entries silently drops
all but one network, leaving containers under-connected on older daemons
such as Docker 20.10 or Synology DSM 7.1/7.2.

Add apiVersion144 constant and wrap the secondary-networks loop in
defaultNetworkSettings so that only the primary network is included in
the ContainerCreate call when the negotiated API version is below 1.44.

Signed-off-by: jarek <jkrochmalski@gmail.com>
This commit is contained in:
jarek 2026-03-31 15:04:46 +02:00 committed by Guillaume Lours
parent 9cab43945a
commit 3b1004c4d9
3 changed files with 44 additions and 6 deletions

View file

@ -19,6 +19,16 @@ package compose
// Docker Engine API version constants.
// These versions correspond to specific Docker Engine releases and their features.
const (
// apiVersion144 represents Docker Engine API version 1.44 (Engine v25.0).
//
// New features in this version:
// - ContainerCreate API accepts multiple EndpointsConfig entries
//
// Before this version:
// - Only a single EndpointsConfig entry was accepted in ContainerCreate
// - Extra networks must be connected individually after container creation via NetworkConnect
apiVersion144 = "1.44"
// apiVersion148 represents Docker Engine API version 1.48 (Engine v28.0).
//
// New features in this version:

View file

@ -563,13 +563,17 @@ func defaultNetworkSettings(project *types.Project,
// so we can pass all the extra networks we want the container to be connected to
// in the network configuration instead of connecting the container to each extra
// network individually after creation.
for _, networkKey := range serviceNetworks {
epSettings, err := createEndpointSettings(project, service, serviceIndex, networkKey, links, useNetworkAliases)
if err != nil {
return "", nil, err
// For older API versions, extra networks are connected via NetworkConnect after
// container creation (see createMobyContainer in convergence.go).
if !versions.LessThan(version, apiVersion144) {
for _, networkKey := range serviceNetworks {
epSettings, err := createEndpointSettings(project, service, serviceIndex, networkKey, links, useNetworkAliases)
if err != nil {
return "", nil, err
}
mobyNetworkName := project.Networks[networkKey].Name
endpointsConfig[mobyNetworkName] = epSettings
}
mobyNetworkName := project.Networks[networkKey].Name
endpointsConfig[mobyNetworkName] = epSettings
}
networkConfig := &network.NetworkingConfig{

View file

@ -273,6 +273,30 @@ func TestDefaultNetworkSettings(t *testing.T) {
assert.Check(t, cmp.Nil(networkConfig))
})
t.Run("returns only primary network in EndpointsConfig for API < 1.44", func(t *testing.T) {
service := composetypes.ServiceConfig{
Name: "myService",
Networks: map[string]*composetypes.ServiceNetworkConfig{
"myNetwork1": {Priority: 10},
"myNetwork2": {Priority: 1000},
},
}
project := composetypes.Project{
Name: "myProject",
Services: composetypes.Services{"myService": service},
Networks: composetypes.Networks(map[string]composetypes.NetworkConfig{
"myNetwork1": {Name: "myProject_myNetwork1"},
"myNetwork2": {Name: "myProject_myNetwork2"},
}),
}
networkMode, networkConfig, err := defaultNetworkSettings(&project, service, 1, nil, true, "1.43")
assert.NilError(t, err)
assert.Equal(t, string(networkMode), "myProject_myNetwork2")
assert.Check(t, cmp.Len(networkConfig.EndpointsConfig, 1))
assert.Check(t, cmp.Contains(networkConfig.EndpointsConfig, "myProject_myNetwork2"))
})
t.Run("returns defined network mode if explicitly set", func(t *testing.T) {
service := composetypes.ServiceConfig{
Name: "myService",