diff --git a/api/v1alpha1/clickhousecluster_types.go b/api/v1alpha1/clickhousecluster_types.go
index 3c642efc..935c56b8 100644
--- a/api/v1alpha1/clickhousecluster_types.go
+++ b/api/v1alpha1/clickhousecluster_types.go
@@ -183,6 +183,10 @@ type ClickHouseSettings struct {
// +optional
TLS ClusterTLSSpec `json:"tls,omitempty"`
+ // Network settings for the ClickHouse server, e.g. the addresses it listens on.
+ // +optional
+ Network NetworkSettings `json:"network,omitempty"`
+
// Enables synchronization of ClickHouse databases to the newly created replicas and cleanup of stale replicas
// after scale down.
// Supports only Replicated and integration databases.
diff --git a/api/v1alpha1/common.go b/api/v1alpha1/common.go
index 1119a62a..51675859 100644
--- a/api/v1alpha1/common.go
+++ b/api/v1alpha1/common.go
@@ -336,6 +336,17 @@ type ClusterTLSSpec struct {
CABundle *CABundleSelector `json:"caBundle,omitempty"`
}
+// NetworkSettings defines ClickHouse server network listening configuration.
+type NetworkSettings struct {
+ // ListenHost is the list of addresses the ClickHouse server listens on.
+ // Use it to conform to networking restrictions, e.g. ["0.0.0.0"] for IPv4-only,
+ // ["::"] for IPv6-only, or ["::", "0.0.0.0"] for dual-stack clusters.
+ // Defaults to ["::", "0.0.0.0"] when empty.
+ // +optional
+ // +listType=atomic
+ ListenHost []string `json:"listenHost,omitempty"`
+}
+
// CABundleSelector selects a key holding a CA bundle from a Secret in the cluster's namespace.
type CABundleSelector struct {
// The name of the secret in the cluster's namespace to select from.
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index c95430ab..3783bdab 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -224,6 +224,7 @@ func (in *ClickHouseSettings) DeepCopyInto(out *ClickHouseSettings) {
}
in.Logger.DeepCopyInto(&out.Logger)
in.TLS.DeepCopyInto(&out.TLS)
+ in.Network.DeepCopyInto(&out.Network)
in.ExtraConfig.DeepCopyInto(&out.ExtraConfig)
in.ExtraUsersConfig.DeepCopyInto(&out.ExtraUsersConfig)
}
@@ -594,6 +595,26 @@ func (in *NamedTemplateMeta) DeepCopy() *NamedTemplateMeta {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *NetworkSettings) DeepCopyInto(out *NetworkSettings) {
+ *out = *in
+ if in.ListenHost != nil {
+ in, out := &in.ListenHost, &out.ListenHost
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkSettings.
+func (in *NetworkSettings) DeepCopy() *NetworkSettings {
+ if in == nil {
+ return nil
+ }
+ out := new(NetworkSettings)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PersistentVolumeClaimTemplate) DeepCopyInto(out *PersistentVolumeClaimTemplate) {
*out = *in
diff --git a/config/crd/bases/clickhouse.com_clickhouseclusters.yaml b/config/crd/bases/clickhouse.com_clickhouseclusters.yaml
index c2965e74..961a65de 100644
--- a/config/crd/bases/clickhouse.com_clickhouseclusters.yaml
+++ b/config/crd/bases/clickhouse.com_clickhouseclusters.yaml
@@ -6454,6 +6454,21 @@ spec:
description: Maximum log file size.
type: string
type: object
+ network:
+ description: Network settings for the ClickHouse server, e.g.
+ the addresses it listens on.
+ properties:
+ listenHost:
+ description: |-
+ ListenHost is the list of addresses the ClickHouse server listens on.
+ Use it to conform to networking restrictions, e.g. ["0.0.0.0"] for IPv4-only,
+ ["::"] for IPv6-only, or ["::", "0.0.0.0"] for dual-stack clusters.
+ Defaults to ["::", "0.0.0.0"] when empty.
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
tls:
description: TLS settings, allows to configure secure endpoints
and certificate verification for ClickHouse server.
diff --git a/dist/chart/templates/crd/clickhouseclusters.clickhouse.com.yaml b/dist/chart/templates/crd/clickhouseclusters.clickhouse.com.yaml
index f2faea51..efd93611 100644
--- a/dist/chart/templates/crd/clickhouseclusters.clickhouse.com.yaml
+++ b/dist/chart/templates/crd/clickhouseclusters.clickhouse.com.yaml
@@ -6457,6 +6457,21 @@ spec:
description: Maximum log file size.
type: string
type: object
+ network:
+ description: Network settings for the ClickHouse server, e.g.
+ the addresses it listens on.
+ properties:
+ listenHost:
+ description: |-
+ ListenHost is the list of addresses the ClickHouse server listens on.
+ Use it to conform to networking restrictions, e.g. ["0.0.0.0"] for IPv4-only,
+ ["::"] for IPv6-only, or ["::", "0.0.0.0"] for dual-stack clusters.
+ Defaults to ["::", "0.0.0.0"] when empty.
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
tls:
description: TLS settings, allows to configure secure endpoints
and certificate verification for ClickHouse server.
diff --git a/docs/reference/api-reference.mdx b/docs/reference/api-reference.mdx
index 43a6c31d..187d9513 100644
--- a/docs/reference/api-reference.mdx
+++ b/docs/reference/api-reference.mdx
@@ -122,6 +122,7 @@ ClickHouseSettings defines ClickHouse server settings options.
| `defaultUserPassword` | [DefaultPasswordSelector](#defaultpasswordselector) | Specifies source and type of the password for `default` ClickHouse user. | false | |
| `logger` | [LoggerConfig](#loggerconfig) | Configuration of ClickHouse server logging. | false | |
| `tls` | [ClusterTLSSpec](#clustertlsspec) | TLS settings, allows to configure secure endpoints and certificate verification for ClickHouse server. | false | |
+| `network` | [NetworkSettings](#networksettings) | Network settings for the ClickHouse server, e.g. the addresses it listens on. | false | |
| `enableDatabaseSync` | boolean | Enables synchronization of ClickHouse databases to the newly created replicas and cleanup of stale replicas
after scale down.
Supports only Replicated and integration databases. | false | true |
| `extraConfig` | [RawExtension](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#rawextension-runtime-pkg) | Additional ClickHouse configuration that will be merged with the default one. | false | |
| `extraUsersConfig` | [RawExtension](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#rawextension-runtime-pkg) | Additional ClickHouse users configuration that will be merged with the default one. | false | |
@@ -353,6 +354,17 @@ NamedTemplateMeta defines supported metadata settings for template objects that
Appears in:
- [PersistentVolumeClaimTemplate](#persistentvolumeclaimtemplate)
+## NetworkSettings {#networksettings}
+
+NetworkSettings defines ClickHouse server network listening configuration.
+
+| Field | Type | Description | Required | Default |
+|-------|------|-------------|----------|---------|
+| `listenHost` | string array | ListenHost is the list of addresses the ClickHouse server listens on.
Use it to conform to networking restrictions, e.g. ["0.0.0.0"] for IPv4-only,
["::"] for IPv6-only, or ["::", "0.0.0.0"] for dual-stack clusters.
Defaults to ["::", "0.0.0.0"] when empty. | false | |
+
+Appears in:
+- [ClickHouseSettings](#clickhousesettings)
+
## PDBPolicy {#pdbpolicy}
PDBPolicy controls whether PodDisruptionBudgets are created.
diff --git a/internal/controller/clickhouse/config.go b/internal/controller/clickhouse/config.go
index ee96212b..bc0bd088 100644
--- a/internal/controller/clickhouse/config.go
+++ b/internal/controller/clickhouse/config.go
@@ -353,7 +353,12 @@ func clusterConfigGenerator(tmpl *template.Template, r *clickhouseReconciler, id
return builder.String(), nil
}
+// defaultListenHost is used when spec.settings.network.listenHost is not set,
+// preserving the historical dual-stack listening behavior.
+var defaultListenHost = []string{"::", "0.0.0.0"}
+
type networkConfigParams struct {
+ ListenHost []string
InterserverHTTPPort uint16
InterserverHTTPUser string
InterserverHTTPPasswordEnvVar string
@@ -381,7 +386,13 @@ func networkConfigGenerator(tmpl *template.Template, r *clickhouseReconciler, _
controllerutil.SortKey(protocols, func(p namedProtocol) string { return p.Name })
+ listenHost := r.Cluster.Spec.Settings.Network.ListenHost
+ if len(listenHost) == 0 {
+ listenHost = defaultListenHost
+ }
+
params := networkConfigParams{
+ ListenHost: listenHost,
InterserverHTTPPort: PortInterserver,
InterserverHTTPUser: InterserverUserName,
InterserverHTTPPasswordEnvVar: EnvInterserverPassword,
diff --git a/internal/controller/clickhouse/config_test.go b/internal/controller/clickhouse/config_test.go
index 9a5e878f..86239a03 100644
--- a/internal/controller/clickhouse/config_test.go
+++ b/internal/controller/clickhouse/config_test.go
@@ -57,3 +57,61 @@ var _ = Describe("ConfigGenerator", func() {
})
}
})
+
+var _ = Describe("networkConfigGenerator listen_host", func() {
+ networkGenerator := func() configGenerator {
+ for _, g := range generators {
+ if g.Filename() == "00-network.yaml" {
+ return g
+ }
+ }
+
+ return nil
+ }
+
+ newReconciler := func(listenHost []string) *clickhouseReconciler {
+ return &clickhouseReconciler{
+ Cluster: &v1.ClickHouseCluster{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test-cluster",
+ Namespace: "test-namespace",
+ },
+ Spec: v1.ClickHouseClusterSpec{
+ Replicas: new(int32(1)),
+ Shards: new(int32(1)),
+ DataVolumeClaimSpec: &corev1.PersistentVolumeClaimSpec{},
+ Settings: v1.ClickHouseSettings{
+ Network: v1.NetworkSettings{ListenHost: listenHost},
+ },
+ },
+ Status: v1.ClickHouseClusterStatus{Version: "25.12.1.1"},
+ },
+ keeper: v1.KeeperCluster{Spec: v1.KeeperClusterSpec{Replicas: new(int32(1))}},
+ }
+ }
+
+ generateListenHost := func(listenHost []string) []any {
+ gen := networkGenerator()
+ Expect(gen).ToNot(BeNil())
+
+ data, err := gen.Generate(newReconciler(listenHost), v1.ClickHouseReplicaID{})
+ Expect(err).ToNot(HaveOccurred())
+
+ obj := map[string]any{}
+ Expect(yaml.Unmarshal([]byte(data), &obj)).To(Succeed())
+ Expect(obj).To(HaveKey("listen_host"))
+
+ hosts, ok := obj["listen_host"].([]any)
+ Expect(ok).To(BeTrue())
+
+ return hosts
+ }
+
+ It("defaults to dual-stack when listenHost is empty", func() {
+ Expect(generateListenHost(nil)).To(Equal([]any{"::", "0.0.0.0"}))
+ })
+
+ It("honors a custom IPv4-only listenHost override", func() {
+ Expect(generateListenHost([]string{"0.0.0.0"})).To(Equal([]any{"0.0.0.0"}))
+ })
+})
diff --git a/internal/controller/clickhouse/templates/network.yaml.tmpl b/internal/controller/clickhouse/templates/network.yaml.tmpl
index f8b87f4d..deb9585f 100644
--- a/internal/controller/clickhouse/templates/network.yaml.tmpl
+++ b/internal/controller/clickhouse/templates/network.yaml.tmpl
@@ -1,6 +1,7 @@
listen_host:
- - "::"
- - "0.0.0.0"
+{{- range .ListenHost }}
+ - "{{ . }}"
+{{- end }}
listen_try: 1
{{- /* can't use composable protocol for interserver, because server disables replication,
if interserver_http_port is not set */}}