Configuration for running management endpoints on a separate server port with independent SSL and settings.
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
import org.springframework.boot.actuate.autoconfigure.web.server.ConditionalOnManagementPort;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextType;# Same port (default)
# No configuration needed
# Separate port
management.server.port=8081
# Disable management
management.server.port=-1| Property Value | Result | Use Case |
|---|---|---|
| Not set | SAME | Default, simplest |
Same as server.port | SAME | Explicit same port |
| Different number | DIFFERENT | Network isolation |
-1 | DISABLED | Disable management |
/**
* Auto-configuration for the management context
* Creates either a same-context or separate child context configuration
* based on management.server.port setting
*
* @since 2.0.0
*/
@AutoConfiguration
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
public final class ManagementContextAutoConfiguration {
/**
* Configuration for when management endpoints run on same port as application
* Validates that management-specific SSL and address are not configured
* Adds 'local.management.port' property alias
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnManagementPort(ManagementPortType.SAME)
static class SameManagementContextConfiguration implements SmartInitializingSingleton {
/**
* Constructor injecting environment for validation
* @param environment Spring environment
*/
SameManagementContextConfiguration(Environment environment);
/**
* Called after all singletons are instantiated
* Performs validation and property alias setup
*/
@Override
void afterSingletonsInstantiated();
/**
* Inner configuration that enables ManagementContextConfiguration imports
* for SAME context type
*/
@Configuration(proxyBeanMethods = false)
@EnableManagementContext(ManagementContextType.SAME)
static class EnableSameManagementContextConfiguration {
}
}
/**
* Configuration for when management endpoints run on different port
* Enables ManagementServerProperties and creates child context initializer
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnManagementPort(ManagementPortType.DIFFERENT)
@EnableConfigurationProperties(ManagementServerProperties.class)
static class DifferentManagementContextConfiguration {
/**
* Creates child management context initializer for separate management port
* ManagementContextFactory is injected as parameter (not created by this config)
* @param managementContextFactory Factory for creating management context
* @param parentContext The parent application context
* @return Child context initializer
*/
@Bean
static ChildManagementContextInitializer childManagementContextInitializer(
ManagementContextFactory managementContextFactory,
AbstractApplicationContext parentContext
);
}
}Key Behavior:
SameManagementContextConfiguration: Active when management.server.port is not set or same as server.port
management.server.ssl.enabled=true)management.server.address is set)local.management.port resolves to local.server.portManagementContextType.SAME configurations via @EnableManagementContextDifferentManagementContextConfiguration: Active when management.server.port is set to a different port
ChildManagementContextInitializer bean for setting up separate management contextManagementServerProperties configuration properties@ConfigurationProperties("management.server")
public class ManagementServerProperties {
private Integer port; // null = same as server.port, -1 = disabled, other = separate port
private InetAddress address; // Bind address (default: same as server.address)
private String basePath = ""; // Additional base path (applied after endpoints.web.base-path)
private Ssl ssl; // SSL configuration (only for separate port)
public @Nullable Ssl getSsl();
public void setSsl(@Nullable Ssl ssl);
}/**
* Type of management port configuration
*/
public enum ManagementPortType {
DISABLED, // management.server.port=-1
SAME, // port not set or same as server.port
DIFFERENT; // separate port specified
/**
* Determines type from environment
* Logic:
* - DISABLED: if management.server.port < 0
* - SAME: if management.server.port is null OR
* (server.port is null AND management.server.port == 8080) OR
* (management.server.port != 0 AND management.server.port == server.port)
* - DIFFERENT: otherwise
*/
public static ManagementPortType get(Environment environment);
}/**
* Base class for customizing web server access logs
* Extends WebServerFactoryCustomizer to allow prefix customization
*
* @param <T> the WebServerFactory type that can be customized
* @since 4.0.0
*/
public abstract class AccessLogCustomizer<T extends WebServerFactory>
implements WebServerFactoryCustomizer<T>, Ordered {
/**
* Creates an access log customizer with optional prefix
* @param prefix Optional prefix to apply to access log file names
*/
protected AccessLogCustomizer(@Nullable String prefix);
/**
* Customizes the access log prefix
* If prefix is null, returns existingPrefix unchanged
* If existingPrefix is null, returns this prefix
* If existingPrefix already starts with this prefix, returns existingPrefix unchanged
* Otherwise, prepends this prefix to existingPrefix
*
* @param existingPrefix The existing prefix (may be null)
* @return The customized prefix (may be null)
*/
protected @Nullable String customizePrefix(@Nullable String existingPrefix);
/**
* Returns the order for this customizer (default: 1)
* @return The order value
*/
@Override
public int getOrder();
}Usage Example:
@Bean
public AccessLogCustomizer<TomcatServletWebServerFactory> managementAccessLogCustomizer() {
return new AccessLogCustomizer<>("management-") {
@Override
public void customize(TomcatServletWebServerFactory factory) {
AccessLogValve valve = new AccessLogValve();
valve.setPrefix(customizePrefix(valve.getPrefix()));
factory.addEngineValves(valve);
}
};
}@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Conditional(OnManagementPortCondition.class)
public @interface ConditionalOnManagementPort {
ManagementPortType value();
}/**
* WebServerFactoryCustomizer that customizes the WebServerFactory used to
* create the management context's web server.
*
* Applies management-specific configuration including port, SSL, address,
* and server header settings. Also inherits parent context customizations
* (e.g., access logs) while resetting error pages.
*
* @param <T> the type of web server factory to customize
* @since 2.0.0
*/
public class ManagementWebServerFactoryCustomizer<T extends ConfigurableWebServerFactory>
implements WebServerFactoryCustomizer<T>, Ordered {
/**
* Creates a new customizer that will retrieve beans using the given beanFactory
* @param beanFactory the bean factory to use
* @since 3.5.0
*/
public ManagementWebServerFactoryCustomizer(ListableBeanFactory beanFactory);
/**
* Customizes the web server factory with management-specific settings
* - Sets management port from ManagementServerProperties
* - Configures SSL if specified in management properties
* - Sets server header from ServerProperties
* - Sets bind address from ManagementServerProperties
* - Resets error pages to empty set
* - Inherits parent context customizations (e.g., access logs)
*
* @param factory the web server factory to customize
*/
@Override
public void customize(T factory);
/**
* Returns the order for this customizer (default: 0)
* @return The order value
*/
@Override
public int getOrder();
/**
* Protected method for subclasses to apply additional customizations
* @param factory the web server factory
* @param managementServerProperties management server properties
* @param serverProperties server properties
*/
protected void customize(T factory, ManagementServerProperties managementServerProperties,
ServerProperties serverProperties);
}Key Behavior:
Usage: This class is used internally by the management context infrastructure. Users typically don't need to interact with it directly, but can extend it for custom management server factory customization.
/**
* Annotation that enables management context configuration import
* Used to import ManagementContextConfigurationImportSelector
* Package-private internal annotation - not part of public API
*
* @since 2.0.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ManagementContextConfigurationImportSelector.class)
@interface EnableManagementContext {
/**
* The type of management context to enable
* @return Management context type
*/
ManagementContextType value();
}/**
* Factory for creating management context instances
* Creates web server application contexts for management endpoints when using separate management port
*
* This is the concrete implementation used by Spring Boot's management context infrastructure.
* For internal use, but can be customized if needed for advanced scenarios.
*
* @since 4.0.0
*/
public final class ManagementContextFactory {
/**
* Creates a management context factory
* @param webApplicationType The web application type (SERVLET or REACTIVE)
* @param webServerFactoryClass The web server factory class to use (e.g., TomcatServletWebServerFactory)
* @param autoConfigurationClasses Auto-configuration classes to apply to the management context
*/
public ManagementContextFactory(
WebApplicationType webApplicationType,
Class<? extends WebServerFactory> webServerFactoryClass,
Class<?>... autoConfigurationClasses
);
/**
* Creates a management context with the given parent context
* @param parentContext The parent application context
* @return The created management application context
*/
public ConfigurableApplicationContext createManagementContext(ApplicationContext parentContext);
/**
* Registers web server factory beans in the management context
* Called during context initialization to set up the web server
* @param parentContext The parent application context
* @param managementContext The management context being configured
* @param registry The annotation config registry for bean registration
*/
public void registerWebServerFactoryBeans(
ApplicationContext parentContext,
ConfigurableApplicationContext managementContext,
AnnotationConfigRegistry registry
);
}Note: This class is primarily for internal use by the management context infrastructure. It is automatically configured when using management.server.port with a different port. Custom management context creation is rarely needed.
/**
* ApplicationContextInitializer that configures the child management context
* Applies when using separate management port (DIFFERENT)
*
* @since 2.0.0
*/
public class ChildManagementContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
/**
* Initializes the child management context
* Sets up parent context reference, environment, and bean factory post-processors
*
* @param applicationContext The child management context to initialize
*/
@Override
public void initialize(ConfigurableApplicationContext applicationContext);
}/**
* ImportSelector that imports management context configurations
*
* Implementation Details:
* - Implements DeferredImportSelector (not just ImportSelector) for late evaluation
* - Reads from META-INF/spring/org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports
* - Filters configurations based on @ManagementContextConfiguration annotation's value()
* - Only imports configurations matching current ManagementContextType:
* - ManagementContextType.SAME: Imports SAME and ANY configurations
* - ManagementContextType.CHILD: Imports CHILD and ANY configurations
* - ManagementContextType.ANY: Always imported regardless of context type
* - Uses Spring's ImportCandidates mechanism for reading imports files
* - Processes @ManagementContextConfiguration annotation on each candidate class
*
* Import File Format:
* - Location: META-INF/spring/org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports
* - Format: One fully-qualified class name per line
* - Comments: Lines starting with # are ignored
* - Each class must be annotated with @ManagementContextConfiguration
*
* @since 2.0.0
*/
public class ManagementContextConfigurationImportSelector implements DeferredImportSelector {
/**
* Selects management context configuration classes to import
*
* Selection Process:
* 1. Read @EnableManagementContext annotation from importing class
* 2. Get ManagementContextType from annotation's value() attribute
* 3. Load candidates from ManagementContextConfiguration.imports file
* 4. For each candidate class:
* a. Check if it has @ManagementContextConfiguration annotation
* b. Get the annotation's value() (context type)
* c. Include if value is ANY or matches current context type
* 5. Return filtered list of configuration class names
*
* @param importingClassMetadata Metadata of the importing class (must have @EnableManagementContext)
* @return Array of filtered configuration class names to import
*/
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata);
}/**
* Condition that matches based on management port type
* Used by @ConditionalOnManagementPort annotation
*
* @since 2.0.0
*/
public class OnManagementPortCondition extends SpringBootCondition {
/**
* Determines the condition outcome based on management port configuration
* Evaluates ManagementPortType.get(environment) against expected value
*
* @param context The condition context
* @param metadata The annotation metadata
* @return Condition outcome (match or no match)
*/
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata);
}# No configuration needed
server.port=8080
# URLs:
# - Application: http://localhost:8080
# - Actuator: http://localhost:8080/actuator/*server.port=8080
management.server.port=8081
# URLs:
# - Application: http://localhost:8080
# - Actuator: http://localhost:8081/actuator/*server.port=8080
management.server.port=8081
management.server.base-path=/admin
management.endpoints.web.base-path=/actuator
# URLs:
# - Application: http://localhost:8080
# - Actuator: http://localhost:8081/admin/actuator/*# Application (HTTP)
server.port=8080
# Management (HTTPS)
management.server.port=8443
management.server.ssl.enabled=true
management.server.ssl.key-store=classpath:management-keystore.p12
management.server.ssl.key-store-password=changeit
management.server.ssl.key-store-type=PKCS12
# URLs:
# - Application: http://localhost:8080
# - Actuator: https://localhost:8443/actuator/*# Application
server.port=8443
server.ssl.enabled=true
server.ssl.key-store=classpath:server-keystore.p12
server.ssl.key-store-password=server-secret
# Management
management.server.port=9443
management.server.ssl.enabled=true
management.server.ssl.key-store=classpath:management-keystore.p12
management.server.ssl.key-store-password=management-secret# Application: all interfaces
server.port=8080
server.address=0.0.0.0
# Management: localhost only
management.server.port=8081
management.server.address=127.0.0.1management.server.port=-1
# All management endpoints disabled| Requirement | Same Port | Separate Port | Reason |
|---|---|---|---|
| Simple setup | ✓ | Less configuration | |
| Network isolation | ✓ | Can restrict firewall access | |
| Different SSL | ✓ | Separate certificates | |
| Localhost-only management | ✓ | Bind to 127.0.0.1 | |
| Docker/K8s health checks | ✓ | Liveness on main port | |
| Internal monitoring | ✓ | Separate security policies |
┌─────────────────────────────────┐
│ Application Context │
│ ┌──────────────────────────┐ │
│ │ Web Server (8080) │ │
│ │ ├─ App Endpoints │ │
│ │ └─ Actuator Endpoints │ │
│ └──────────────────────────┘ │
└─────────────────────────────────┘┌──────────────────────────────────────┐
│ Parent Application Context │
│ ┌──────────────────────────────┐ │
│ │ Main Web Server (8080) │ │
│ │ └─ App Endpoints │ │
│ └──────────────────────────────┘ │
│ │
│ ┌──────────────────────────────┐ │
│ │ Child Management Context │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ Mgmt Server (8081) │ │ │
│ │ │ └─ Actuator Endpoints │ │ │
│ │ └─────────────────────────┘ │ │
│ └──────────────────────────────┘ │
└──────────────────────────────────────┘@Bean
@ConditionalOnManagementPort(ManagementPortType.SAME)
public SamePortSecurityFilter samePortSecurityFilter() {
return new SamePortSecurityFilter();
}@Bean
@ConditionalOnManagementPort(ManagementPortType.DIFFERENT)
public ManagementPortSecurityFilter managementPortSecurityFilter() {
return new ManagementPortSecurityFilter();
}@Bean
@ConditionalOnManagementPort(ManagementPortType.DISABLED)
public NoOpManagementConfiguration noOpConfig() {
return new NoOpManagementConfiguration();
}public enum ManagementContextType {
SAME, // Endpoints in same context as application
CHILD, // Endpoints in separate child context
ANY // Applies to both
}@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Configuration
public @interface ManagementContextConfiguration {
ManagementContextType value() default ManagementContextType.ANY;
boolean proxyBeanMethods() default true;
}File: META-INF/spring/org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports
com.example.CommonManagementConfiguration
com.example.ChildManagementConfiguration// Applies to both SAME and CHILD
@ManagementContextConfiguration
public class CommonManagementConfiguration {
@Bean
public CommonBean commonBean() {
return new CommonBean();
}
}
// Only for CHILD (separate port)
@ManagementContextConfiguration(ManagementContextType.CHILD)
public class ChildManagementConfiguration {
@Bean
public ChildOnlyBean childOnlyBean() {
return new ChildOnlyBean();
}
}@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/**").hasRole("ACTUATOR")
.anyRequest().authenticated());
return http.build();
}@Configuration
public class SecurityConfiguration {
// Main application security (port 8080)
@Bean
@Order(1)
public SecurityFilterChain appSecurityFilterChain(HttpSecurity http) throws Exception {
http.securityMatcher(request -> request.getServerPort() == 8080)
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated());
return http.build();
}
// Management security (port 8081)
@Bean
@Order(2)
public SecurityFilterChain managementSecurityFilterChain(HttpSecurity http) throws Exception {
http.securityMatcher(request -> request.getServerPort() == 8081)
.authorizeHttpRequests(auth -> auth.anyRequest().hasRole("ACTUATOR"));
return http.build();
}
}Symptom: Address already in use on management port
Solution:
# Change port or use dynamic port
management.server.port=8082
# Or use 0 for random port
management.server.port=0Cause: Wrong port or base path
Check URL Structure:
<protocol>://<address>:<management.server.port><management.server.base-path><management.endpoints.web.base-path>/<endpoint>
Example:
http://localhost:8081/admin/actuator/healthCause: Wrong keystore or password
Debug:
logging.level.org.springframework.boot.web.embedded.tomcat.TomcatWebServer=DEBUGCause: Bean defined in wrong context
Solution: Use @ManagementContextConfiguration for management-only beans
# Simple same-port setup
server.port=8080
management.endpoints.web.exposure.include=*# Separate port with network isolation
server.port=8080
server.address=0.0.0.0
management.server.port=8081
management.server.address=10.0.0.10 # Internal network only
management.endpoints.web.exposure.include=health,info,metrics# Health checks on main port
management.endpoint.health.probes.enabled=true
server.port=8080
# Full actuator on separate port (not exposed externally)
management.server.port=8081
management.endpoints.web.exposure.include=*# Main app: public HTTPS
server.port=443
server.ssl.enabled=true
# Management: internal network, different SSL
management.server.port=8443
management.server.address=10.0.0.10
management.server.ssl.enabled=true
management.server.ssl.key-store=classpath:internal-keystore.p12