Summary
The operator always sets containerPort to the constant 8435 (ConfigReloaderDefaultPort in internal/controller/operator/factory/build/container.go) when building the config-reloader sidecar. Changing the process listen address with spec.configReloaderExtraArgs["http.listenAddr"] updates container args only. Neither containerPort nor the generated HTTP liveness/readiness probes respect the overridden port — both stay locked to 8435, so the kube-scheduler still sees a host port conflict even when the process listens elsewhere.
On nodes with hostNetwork: true, that blocks scheduling a second DaemonSet that also needs a config-reloader: Kubernetes reports that the node did not have free ports for the requested pod ports (didn't have free ports…), even when one process listens on 8435 and another on 8436 via http.listenAddr.
CommonConfigReloaderParams is shared across VMAgent, VMAuth, VMSingle, VMAlertmanager, and VMAlert. The bug lives in the common builder build.ConfigReloaderContainer and affects any component that uses configReloaderExtraArgs to change the listen address.
Environment
- Operator: v0.74.0 (also reproduced in source of v0.74.1, v0.75.0-rc1, and current
master as of 2026-09-04)
- Resources: two
VMAgent DaemonSets with hostNetwork: true on the same node
- Sidecar: default VictoriaMetrics config-reloader
Steps to reproduce
- Deploy two
VMAgent CRs as DaemonSets with hostNetwork: true (and otherwise valid scrape config).
- On the second agent, override the reloader listen port, for example:
spec:
hostNetwork: true
configReloaderExtraArgs:
http.listenAddr: "127.0.0.1:8436"
- Observe PodSpec of the second DaemonSet:
config-reloader still has containerPort: 8435 and probes targeting 8435, while args include --http.listenAddr=127.0.0.1:8436.
- Scheduler fails to place the second pod on a node that already runs the first agent’s reloader on host port 8435.
Expected
ContainerPort and HTTP probe ports for config-reloader should follow the port from configReloaderExtraArgs["http.listenAddr"] when set (with fallback to 8435).
Actual
Process listens on the overridden address (127.0.0.1:8436). PodSpec still declares containerPort: 8435 and probes 8435. Scheduler rejects the pod on a node that already exposes host port 8435 for another agent’s reloader.
Proposed fix
- Parse the port from
ConfigReloaderExtraArgs["http.listenAddr"] (support :PORT, IP:PORT, [IPv6]:PORT).
- Use that port for
ContainerPort and liveness/readiness probes in addPortProbesToConfigReloaderContainer (default 8435 if unset/invalid).
- Align scrape endpoint builders that hardcode
8435 if they scrape the sidecar by numeric port.
- Update
configReloaderMetricsURL in internal/controller/operator/factory/reconcile/config_reload.go, which also hardcodes build.ConfigReloaderDefaultPort (8435). Controllers that call WaitForConfigReloadHash (e.g. VMAuth) poll /metrics on that URL; when the sidecar listen port is overridden, they keep polling 8435 and eventually time out. configReloaderMetricsURL should resolve the configured port the same way as ContainerPort and probes.
- Add unit tests in
container_test.go for default, overridden, IPv6, and invalid values.
Related
Related code
internal/controller/operator/factory/build/container.go: ConfigReloaderDefaultPort, ConfigReloaderContainer, addPortProbesToConfigReloaderContainer
Summary
The operator always sets
containerPortto the constant8435(ConfigReloaderDefaultPortininternal/controller/operator/factory/build/container.go) when building theconfig-reloadersidecar. Changing the process listen address withspec.configReloaderExtraArgs["http.listenAddr"]updates container args only. NeithercontainerPortnor the generated HTTP liveness/readiness probes respect the overridden port — both stay locked to8435, so the kube-scheduler still sees a host port conflict even when the process listens elsewhere.On nodes with
hostNetwork: true, that blocks scheduling a second DaemonSet that also needs a config-reloader: Kubernetes reports that the node did not have free ports for the requested pod ports (didn't have free ports…), even when one process listens on8435and another on8436viahttp.listenAddr.CommonConfigReloaderParamsis shared acrossVMAgent,VMAuth,VMSingle,VMAlertmanager, andVMAlert. The bug lives in the common builderbuild.ConfigReloaderContainerand affects any component that usesconfigReloaderExtraArgsto change the listen address.Environment
masteras of 2026-09-04)VMAgentDaemonSets withhostNetwork: trueon the same nodeSteps to reproduce
VMAgentCRs as DaemonSets withhostNetwork: true(and otherwise valid scrape config).config-reloaderstill hascontainerPort: 8435and probes targeting8435, while args include--http.listenAddr=127.0.0.1:8436.Expected
ContainerPortand HTTP probe ports forconfig-reloadershould follow the port fromconfigReloaderExtraArgs["http.listenAddr"]when set (with fallback to8435).Actual
Process listens on the overridden address (
127.0.0.1:8436). PodSpec still declarescontainerPort: 8435and probes8435. Scheduler rejects the pod on a node that already exposes host port 8435 for another agent’s reloader.Proposed fix
ConfigReloaderExtraArgs["http.listenAddr"](support:PORT,IP:PORT,[IPv6]:PORT).ContainerPortand liveness/readiness probes inaddPortProbesToConfigReloaderContainer(default8435if unset/invalid).8435if they scrape the sidecar by numeric port.configReloaderMetricsURLininternal/controller/operator/factory/reconcile/config_reload.go, which also hardcodesbuild.ConfigReloaderDefaultPort(8435). Controllers that callWaitForConfigReloadHash(e.g.VMAuth) poll/metricson that URL; when the sidecar listen port is overridden, they keep polling8435and eventually time out.configReloaderMetricsURLshould resolve the configured port the same way asContainerPortand probes.container_test.gofor default, overridden, IPv6, and invalid values.Related
enableTCP6/http.listenAddrinteraction (different symptom; related listen-address handling).ContainerPortscheduling).Related code
internal/controller/operator/factory/build/container.go:ConfigReloaderDefaultPort,ConfigReloaderContainer,addPortProbesToConfigReloaderContainer