diff --git a/daemon/config/config_test.go b/daemon/config/config_test.go
index 24d8093fc4..0af8883ff3 100644
--- a/daemon/config/config_test.go
+++ b/daemon/config/config_test.go
@@ -8,6 +8,7 @@ import (
 
 	"github.com/docker/docker/daemon/discovery"
 	"github.com/docker/docker/opts"
+	"github.com/docker/libnetwork/ipamutils"
 	"github.com/spf13/pflag"
 	"gotest.tools/v3/assert"
 	is "gotest.tools/v3/assert/cmp"
@@ -153,6 +154,48 @@ func TestDaemonConfigurationMergeConflictsWithInnerStructs(t *testing.T) {
 	}
 }
 
+// Test for #40711
+func TestDaemonConfigurationMergeDefaultAddressPools(t *testing.T) {
+	emptyConfigFile := fs.NewFile(t, "config", fs.WithContent(`{}`))
+	defer emptyConfigFile.Remove()
+	configFile := fs.NewFile(t, "config", fs.WithContent(`{"default-address-pools":[{"base": "10.123.0.0/16", "size": 24 }]}`))
+	defer configFile.Remove()
+
+	expected := []*ipamutils.NetworkToSplit{{Base: "10.123.0.0/16", Size: 24}}
+
+	t.Run("empty config file", func(t *testing.T) {
+		var conf = Config{}
+		flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
+		flags.Var(&conf.NetworkConfig.DefaultAddressPools, "default-address-pool", "")
+		flags.Set("default-address-pool", "base=10.123.0.0/16,size=24")
+
+		config, err := MergeDaemonConfigurations(&conf, flags, emptyConfigFile.Path())
+		assert.NilError(t, err)
+		assert.DeepEqual(t, config.DefaultAddressPools.Value(), expected)
+	})
+
+	t.Run("config file", func(t *testing.T) {
+		var conf = Config{}
+		flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
+		flags.Var(&conf.NetworkConfig.DefaultAddressPools, "default-address-pool", "")
+
+		config, err := MergeDaemonConfigurations(&conf, flags, configFile.Path())
+		assert.NilError(t, err)
+		assert.DeepEqual(t, config.DefaultAddressPools.Value(), expected)
+	})
+
+	t.Run("with conflicting options", func(t *testing.T) {
+		var conf = Config{}
+		flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
+		flags.Var(&conf.NetworkConfig.DefaultAddressPools, "default-address-pool", "")
+		flags.Set("default-address-pool", "base=10.123.0.0/16,size=24")
+
+		_, err := MergeDaemonConfigurations(&conf, flags, configFile.Path())
+		assert.ErrorContains(t, err, "the following directives are specified both as a flag and in the configuration file")
+		assert.ErrorContains(t, err, "default-address-pools")
+	})
+}
+
 func TestFindConfigurationConflictsWithUnknownKeys(t *testing.T) {
 	config := map[string]interface{}{"tls-verify": "true"}
 	flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
diff --git a/opts/address_pools.go b/opts/address_pools.go
index 9b27a62853..fa15c24b98 100644
--- a/opts/address_pools.go
+++ b/opts/address_pools.go
@@ -12,12 +12,12 @@ import (
 
 // PoolsOpt is a Value type for parsing the default address pools definitions
 type PoolsOpt struct {
-	values []*types.NetworkToSplit
+	Values []*types.NetworkToSplit
 }
 
 // UnmarshalJSON fills values structure  info from JSON input
 func (p *PoolsOpt) UnmarshalJSON(raw []byte) error {
-	return json.Unmarshal(raw, &(p.values))
+	return json.Unmarshal(raw, &(p.Values))
 }
 
 // Set predefined pools
@@ -53,7 +53,7 @@ func (p *PoolsOpt) Set(value string) error {
 		}
 	}
 
-	p.values = append(p.values, &poolsDef)
+	p.Values = append(p.Values, &poolsDef)
 
 	return nil
 }
@@ -66,7 +66,7 @@ func (p *PoolsOpt) Type() string {
 // String returns a string repr of this option
 func (p *PoolsOpt) String() string {
 	var pools []string
-	for _, pool := range p.values {
+	for _, pool := range p.Values {
 		repr := fmt.Sprintf("%s %d", pool.Base, pool.Size)
 		pools = append(pools, repr)
 	}
@@ -75,7 +75,7 @@ func (p *PoolsOpt) String() string {
 
 // Value returns the mounts
 func (p *PoolsOpt) Value() []*types.NetworkToSplit {
-	return p.values
+	return p.Values
 }
 
 // Name returns the flag name of this option