Below worked fine with Spring Boot 2.7.11 and earlier, stopped working with Spring Boot 3.

http://host:8989/metrics

{"timestamp":"2023-05-15T08:17:22.903+00:00","status":404,"error":"Not Found","path":"/metrics/"}

Tested other health endpoint with root - works as expected.

management.server.port=8989
management.server.base-path=/metrics
management.endpoints.web.base-path=/
management.endpoints.web.path-mapping.prometheus=/

Works for health:

management.server.port=8989
management.server.base-path=/metrics
management.endpoints.web.base-path=/
management.endpoints.web.path-mapping.health=/

Was asked to open in spring boot by micrometer owners: https://github.com/micrometer-metrics/micrometer/issues/3839

Comment From: wilkinsona

Thanks for the report but I cannot reproduce the behavior that you have described.

With 2.7.11, the server does not start when using the configuration properties that you have shared:


  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::               (v2.7.11)

2023-05-15 20:41:31.108  INFO 65152 --- [           main] com.example.demo.Gh35426Application      : Starting Gh35426Application using Java 17.0.1 on wilkinsonaGMD6R.vmware.com with PID 65152 (/Users/awilkinson/dev/workspaces/spring-projects/spring-boot/3.0.x/gh-35426/bin/main started by awilkinson in /Users/awilkinson/dev/workspaces/spring-projects/spring-boot/3.0.x/gh-35426)
2023-05-15 20:41:31.110  INFO 65152 --- [           main] com.example.demo.Gh35426Application      : No active profile set, falling back to 1 default profile: "default"
2023-05-15 20:41:31.858  INFO 65152 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2023-05-15 20:41:31.866  INFO 65152 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-05-15 20:41:31.867  INFO 65152 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.74]
2023-05-15 20:41:31.943  INFO 65152 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-05-15 20:41:31.943  INFO 65152 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 795 ms
2023-05-15 20:41:32.405  INFO 65152 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2023-05-15 20:41:32.456  INFO 65152 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8989 (http)
2023-05-15 20:41:32.457  INFO 65152 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-05-15 20:41:32.457  INFO 65152 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.74]
2023-05-15 20:41:32.470  INFO 65152 --- [           main] o.a.c.c.C.[.[localhost].[/metrics]       : Initializing Spring embedded WebApplicationContext
2023-05-15 20:41:32.470  INFO 65152 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 62 ms
2023-05-15 20:41:32.481  INFO 65152 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 1 endpoint(s) beneath base path ''
2023-05-15 20:41:32.484  WARN 65152 --- [           main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webEndpointServletHandlerMapping' defined in class path resource [org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'Actuator root web endpoint' method 
Actuator root web endpoint
to {GET [], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}: There is already 'Actuator web endpoint 'health'' bean method
Actuator web endpoint 'health' mapped.
2023-05-15 20:41:32.487  INFO 65152 --- [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2023-05-15 20:41:32.491  WARN 65152 --- [           main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'webServerStartStop'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webEndpointServletHandlerMapping' defined in class path resource [org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'Actuator root web endpoint' method 
Actuator root web endpoint
to {GET [], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}: There is already 'Actuator web endpoint 'health'' bean method
Actuator web endpoint 'health' mapped.
2023-05-15 20:41:32.502  INFO 65152 --- [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2023-05-15 20:41:32.514  INFO 65152 --- [           main] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2023-05-15 20:41:32.531 ERROR 65152 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.context.ApplicationContextException: Failed to start bean 'webServerStartStop'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webEndpointServletHandlerMapping' defined in class path resource [org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'Actuator root web endpoint' method 
Actuator root web endpoint
to {GET [], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}: There is already 'Actuator web endpoint 'health'' bean method
Actuator web endpoint 'health' mapped.
    at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181) ~[spring-context-5.3.27.jar:5.3.27]
    at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54) ~[spring-context-5.3.27.jar:5.3.27]
    at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356) ~[spring-context-5.3.27.jar:5.3.27]
    at java.base/java.lang.Iterable.forEach(Iterable.java:75) ~[na:na]
    at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155) ~[spring-context-5.3.27.jar:5.3.27]
    at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123) ~[spring-context-5.3.27.jar:5.3.27]
    at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:937) ~[spring-context-5.3.27.jar:5.3.27]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586) ~[spring-context-5.3.27.jar:5.3.27]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.11.jar:2.7.11]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) ~[spring-boot-2.7.11.jar:2.7.11]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.11.jar:2.7.11]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) ~[spring-boot-2.7.11.jar:2.7.11]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) ~[spring-boot-2.7.11.jar:2.7.11]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) ~[spring-boot-2.7.11.jar:2.7.11]
    at com.example.demo.Gh35426Application.main(Gh35426Application.java:10) ~[main/:na]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webEndpointServletHandlerMapping' defined in class path resource [org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'Actuator root web endpoint' method 
Actuator root web endpoint
to {GET [], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}: There is already 'Actuator web endpoint 'health'' bean method
Actuator web endpoint 'health' mapped.
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1804) ~[spring-beans-5.3.27.jar:5.3.27]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620) ~[spring-beans-5.3.27.jar:5.3.27]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.27.jar:5.3.27]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.27.jar:5.3.27]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.27.jar:5.3.27]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.27.jar:5.3.27]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.27.jar:5.3.27]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955) ~[spring-beans-5.3.27.jar:5.3.27]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:920) ~[spring-context-5.3.27.jar:5.3.27]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.27.jar:5.3.27]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.11.jar:2.7.11]
    at org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration$DifferentManagementContextConfiguration.onApplicationEvent(ManagementContextAutoConfiguration.java:149) ~[spring-boot-actuator-autoconfigure-2.7.11.jar:2.7.11]
    at org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration$DifferentManagementContextConfiguration.onApplicationEvent(ManagementContextAutoConfiguration.java:122) ~[spring-boot-actuator-autoconfigure-2.7.11.jar:2.7.11]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176) ~[spring-context-5.3.27.jar:5.3.27]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169) ~[spring-context-5.3.27.jar:5.3.27]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143) ~[spring-context-5.3.27.jar:5.3.27]
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:421) ~[spring-context-5.3.27.jar:5.3.27]
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:378) ~[spring-context-5.3.27.jar:5.3.27]
    at org.springframework.boot.web.servlet.context.WebServerStartStopLifecycle.start(WebServerStartStopLifecycle.java:46) ~[spring-boot-2.7.11.jar:2.7.11]
    at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178) ~[spring-context-5.3.27.jar:5.3.27]
    ... 14 common frames omitted
Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'Actuator root web endpoint' method 
Actuator root web endpoint
to {GET [], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}: There is already 'Actuator web endpoint 'health'' bean method
Actuator web endpoint 'health' mapped.
    at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.validateMethodMapping(AbstractHandlerMethodMapping.java:669) ~[spring-webmvc-5.3.27.jar:5.3.27]
    at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.register(AbstractHandlerMethodMapping.java:635) ~[spring-webmvc-5.3.27.jar:5.3.27]
    at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.registerMapping(AbstractHandlerMethodMapping.java:189) ~[spring-webmvc-5.3.27.jar:5.3.27]
    at org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.registerLinksMapping(AbstractWebMvcEndpointHandlerMapping.java:252) ~[spring-boot-actuator-2.7.11.jar:2.7.11]
    at org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.initHandlerMethods(AbstractWebMvcEndpointHandlerMapping.java:182) ~[spring-boot-actuator-2.7.11.jar:2.7.11]
    at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.afterPropertiesSet(AbstractHandlerMethodMapping.java:213) ~[spring-webmvc-5.3.27.jar:5.3.27]
    at org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.afterPropertiesSet(AbstractWebMvcEndpointHandlerMapping.java:171) ~[spring-boot-actuator-2.7.11.jar:2.7.11]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863) ~[spring-beans-5.3.27.jar:5.3.27]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800) ~[spring-beans-5.3.27.jar:5.3.27]
    ... 33 common frames omitted

With 3.0.6 the server starts but a request to localhost:8989/metrics/ returns the hypermedia links to all of the actuator's endpoints.

If you would like us to spend some more time investigating, please spend some time providing a complete yet minimal sample that reproduces the behavior that you have described. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.

Comment From: pavelorehov

Created small demo app where it is reproduces with spring boot 3. Added comments in pom.xml how to downgrade to 2.7.11 where it works perfect. https://github.com/pavelorehov/sb3prometheus/tree/main/demo

Comment From: wilkinsona

Thank you. I've now reproduced the behaviour that you have described. The following properties were missing from those shared above:

management.endpoints.web.discovery.enabled=false
management.endpoints.web.exposure.include=*

I believe the change in behaviour is due to https://github.com/spring-projects/spring-framework/issues/28552. The health endpoint isn't affected as its operations mean that it has a mapping for /**. Most other endpoints have no such mapping and they also cannot be mapped to the root any more.

When the base path and an endpoint's path mapping are set to /, the resulting pattern for the endpoint is an empty string. This then clashes with Tomcat redirecting requests to the context root by appending a /. In the context of this issue, this means that a request to http://localhost:8989/metrics is redirected to http://localhost:8989/metrics/ which doesn't match. As a workaround, the problem does not occur with 3.0.x if server.tomcat.redirect-context-root is set to false.

https://github.com/spring-projects/spring-boot/issues/31563 tracked Boot adapting to the changes in Framework but this looks like a scenario that we didn't consider. We'll have to review the changes that we made and see if we can accommodate this scenario without breaking anything else.

Comment From: pavelorehov

Thank you, can server.tomcat.redirect-context-root=false be temporary WA until you find permanent solution ?

Comment From: wilkinsona

Yes, it's certainly worth trying to see if it meets your needs.

Comment From: wilkinsona

@Eddyzhang7 That seems unrelated to this issue as you don't appear to have mapped anything to /. If you believe you have found a bug, please open a new issue and provide a minimal sample that reproduces the problem.