spring-boot-admin-docs/src/site/docs/03-client/20-registration.md
The Spring Boot Admin Client handles the registration of your application with the Admin Server through the
ApplicationRegistrator and ApplicationFactory interfaces.
The ApplicationRegistrator is responsible for managing the registration lifecycle:
public interface ApplicationRegistrator {
/**
* Registers the client application at spring-boot-admin-server.
* @return true if successful registration on at least one admin server
*/
boolean register();
/**
* Tries to deregister currently registered application
*/
void deregister();
/**
* @return the id of this client as given by the admin server.
* Returns null if not registered yet.
*/
String getRegisteredId();
}
The DefaultApplicationRegistrator automatically handles:
spring:
boot:
admin:
client:
url: http://localhost:8080 # Admin Server URL
period: 10000 # Registration interval in milliseconds
auto-registration: true # Enable auto-registration
auto-deregistration: true # Enable auto-deregistration on shutdown
RegistrationApplicationListener triggers registration when WebServerInitializedEvent
is firedApplicationFactory creates the registration payload/instances endpointThe ApplicationFactory is responsible for creating the Application object that contains all registration
information.
public interface ApplicationFactory {
Application createApplication();
}
The default factory gathers information from:
InstanceProperties - Client configurationServerProperties - Web server configurationManagementServerProperties - Actuator configurationPathMappedEndpoints - Actuator endpoint mappingsMetadataContributor - Custom metadata@Override
public Application createApplication() {
return Application.create(getName())
.healthUrl(getHealthUrl())
.managementUrl(getManagementUrl())
.serviceUrl(getServiceUrl())
.metadata(getMetadata())
.build();
}
spring:
boot:
admin:
client:
instance:
name: ${spring.application.name} # Application name
The URL where your application can be accessed:
spring:
boot:
admin:
client:
instance:
service-url: https://my-app.example.com
# or let it auto-detect:
service-base-url: https://my-app.example.com
service-path: /
Auto-detection uses:
service-url (highest priority)service-base-url + service-pathURL for actuator endpoints:
spring:
boot:
admin:
client:
instance:
management-url: https://my-app.example.com/actuator
# or
management-base-url: https://my-app.example.com
management:
endpoints:
web:
base-path: /actuator
Specific health endpoint URL:
spring:
boot:
admin:
client:
instance:
health-url: https://my-app.example.com/actuator/health
Control how the service host is determined:
spring:
boot:
admin:
client:
instance:
service-host-type: IP # or CANONICAL
IP: Use the IP addressCANONICAL: Use the canonical hostnameCreate a custom factory for specialized registration logic:
@Component
public class CustomApplicationFactory implements ApplicationFactory {
private final InstanceProperties instance;
private final Environment environment;
public CustomApplicationFactory(InstanceProperties instance,
Environment environment) {
this.instance = instance;
this.environment = environment;
}
@Override
public Application createApplication() {
Map<String, String> metadata = new HashMap<>();
metadata.put("environment", environment.getProperty("app.environment"));
metadata.put("version", environment.getProperty("app.version"));
metadata.put("region", environment.getProperty("cloud.region"));
return Application.create(instance.getName())
.healthUrl(buildHealthUrl())
.managementUrl(buildManagementUrl())
.serviceUrl(buildServiceUrl())
.metadata(metadata)
.build();
}
private String buildHealthUrl() {
// Custom logic to build health URL
return "https://my-app.com/health";
}
private String buildManagementUrl() {
// Custom logic to build management URL
return "https://my-app.com/management";
}
private String buildServiceUrl() {
// Custom logic to build service URL
return "https://my-app.com";
}
}
For servlet-based applications:
public class ServletApplicationFactory extends DefaultApplicationFactory {
// Detects servlet port and context path automatically
}
For WebFlux applications:
public class ReactiveApplicationFactory extends DefaultApplicationFactory {
// Detects Netty port and context automatically
}
For Cloud Foundry deployments:
public class CloudFoundryApplicationFactory implements ApplicationFactory {
// Uses CF-specific environment variables:
// - vcap.application.application_id
// - vcap.application.instance_id
// - vcap.application.uris
}
Automatically activated when Cloud Foundry is detected.
The Application class represents the registration payload:
public class Application {
private final String name;
private final String managementUrl;
private final String healthUrl;
private final String serviceUrl;
private final Map<String, String> metadata;
// Builder pattern
public static Builder create(String name) {
return new Builder(name);
}
}
Application app = Application.create("my-application")
.healthUrl("http://localhost:8080/actuator/health")
.managementUrl("http://localhost:8080/actuator")
.serviceUrl("http://localhost:8080")
.metadata("version", "1.0.0")
.metadata("environment", "production")
.build();
Spring Boot Admin Client fires application events during registration:
@Component
public class RegistrationEventListener {
@EventListener
public void onRegistration(InstanceRegisteredEvent event) {
String instanceId = event.getRegistration().getInstanceId();
log.info("Registered with instance ID: {}", instanceId);
}
@EventListener
public void onDeregistration(InstanceDeregisteredEvent event) {
log.info("Deregistered instance");
}
}
Implement custom registration logic:
@Component
public class CustomApplicationRegistrator implements ApplicationRegistrator {
private final ApplicationFactory applicationFactory;
private final RestClient restClient;
private final String adminUrl;
private volatile String registeredId;
@Override
public boolean register() {
Application application = applicationFactory.createApplication();
try {
Map<String, Object> response = restClient.post()
.uri(adminUrl + "/instances")
.body(application)
.retrieve()
.body(new ParameterizedTypeReference<Map<String, Object>>() {});
this.registeredId = (String) response.get("id");
log.info("Registered as: {}", registeredId);
return true;
} catch (Exception e) {
log.error("Registration failed", e);
return false;
}
}
@Override
public void deregister() {
if (registeredId != null) {
try {
restClient.delete()
.uri(adminUrl + "/instances/" + registeredId)
.retrieve()
.toBodilessEntity();
log.info("Deregistered successfully");
} catch (Exception e) {
log.error("Deregistration failed", e);
}
}
}
@Override
public String getRegisteredId() {
return registeredId;
}
}
Check:
Verify:
This is normal behavior - the client re-registers periodically as a heartbeat. Adjust the period if needed:
spring:
boot:
admin:
client:
period: 30000 # 30 seconds instead of default 10