Since spring-cloud-context 5.0.2, ConfigurationPropertiesRebinder resets every rebindable bean to
the defaults of a template instance before re-binding it. Whatever was set programmatically after
the initial binding is gone, because the re-binding only restores what the Environment carries.
EurekaInstanceConfigBean loses ipAddress, metadataMap and nonSecurePort. The instance is then
cancelled from the registry and cannot register again: the server answers 400.
Java 25, Spring Boot 4.1.1, Spring Cloud 2025.1.3, spring-cloud-context 5.0.3, spring-cloud-netflix-eureka-client 5.0.2.
demo-eureka (8761), demo-configserver (8888), then demo-refresh (8080), which contains no code
beyond the generated @SpringBootApplication.
cd demo-eureka && ./mvnw spring-boot:run
cd demo-configserver && ./mvnw spring-boot:run
cd demo-refresh && ./mvnw spring-boot:run \
-Dspring-boot.run.jvmArguments="-Dspring.application.instance_id=xxx" \
-Dspring-boot.run.arguments="--management.endpoint.configprops.show-values=ALWAYS"
props() { curl -s localhost:8080/actuator/configprops \
| jq -c '..|objects|select(.prefix=="eureka.instance")|.properties
|{hostname,ipAddress,metadataMap,nonSecurePort}'; }
props; curl -s -X POST localhost:8080/actuator/refresh; propsrefresh -> [] no key of the Environment changed
BEFORE AFTER
hostname : localhost hostname : localhost
ipAddress : 192.168.1.117 ipAddress : null
metadataMap : {management.port=8080} metadataMap : {}
nonSecurePort : 8080 nonSecurePort : 80
An empty change set is not a condition, only the shortest proof that the refresh itself drives the
reset. hostname survives because application.yaml carries it.
Registered instance DEMO-REFRESH/demo-refresh:8080:xxx with status UP
Cancelled instance DEMO-REFRESH/demo-refresh:8080:xxx
registering service... -> status code 400 -> registration failed
was unable to send heartbeat!
ApplicationResource.addInstance answers 400 Missing ip address for a blank ipAddr. The Eureka
server runs with defaults; the run logs one explicit cancel, zero evictions.
resetBeanToDefaults builds its template through any no-arg constructor, whatever its visibility:
for (Constructor<?> constructor : type.getDeclaredConstructors()) {
if (constructor.getParameterCount() == 0) { return true; }
}EurekaInstanceConfigBean has a private EurekaInstanceConfigBean() {} next to the public
EurekaInstanceConfigBean(InetUtils) that computes ipAddress and hostname. The template is built
with the private one, so it carries ipAddress = null, nonSecurePort = 80 and an empty
metadataMap, and those get written onto the live bean. The re-binding restores nothing, because:
ipAddresscomes from the public constructor,nonSecurePortfromEurekaAutoServiceRegistration(Updating port to 8080),metadataMap[management.port]fromEurekaClientAutoConfiguration:204.
rebind() iterates every bean whatever the changed keys, so /actuator/busrefresh behaves the same.
The registration breaks because eureka.client.refresh.enable defaults to true: EurekaClient and
ApplicationInfoManager are refresh scoped, refreshAll() destroys them, the shutdown cancels the
lease, and the new InstanceInfo is built from the reset bean.
Absent in 5.0.1, introduced in 5.0.2 by #1680, extended in 5.0.3.
cd demo-refresh && ./mvnw spring-boot:run -Pcontext-5.0.1 \
-Dspring-boot.run.jvmArguments="-Dspring.application.instance_id=xxx"With 5.0.1 the same refresh leaves the bean untouched. #1680 targets a property removed from the
Environment while the bean keeps its value; the reset cannot tell that apart from a value that was
never in the Environment.
spring.cloud.refresh.never-refreshable=com.zaxxer.hikari.HikariDataSource,org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBeanskips the bean. The property replaces the default, so Hikari has to be repeated.eureka.client.refresh.enable=falsedoes not prevent the reset, it only stops anything from rebuilding anInstanceInfofrom the reset bean.- Declaring the values under
eureka.instance.*puts them where the re-binding can restore them.
#1727, same pull request: nested objects pass through null during the rebind and concurrent threads
get an NPE. Transient, traffic dependent. This one is the state the bean is left in, on an idle
application.