How does Spring Boot choose and configure an embedded servlet container (Tomcat/Jetty/Undertow)?
Quick Answer
spring-boot-starter-web includes Tomcat as the default embedded servlet container; swapping to Jetty or Undertow just means excluding the Tomcat starter dependency and adding the corresponding Jetty/Undertow starter instead — auto-configuration then detects which one is on the classpath and configures that. The container runs inside the same JVM process as the application (embedded, not a separately installed/deployed server), configured via standard application.properties/yml keys like server.port, and its lifecycle is tied to the Spring ApplicationContext's own startup/shutdown.
Detailed Answer
Unlike traditional Java EE deployment (build a WAR, deploy it to a separately-installed application server), Spring Boot embeds the servlet container directly inside the application's own runnable JAR — the server runs as a library, inside the same JVM process, started by SpringApplication.run().
Default: spring-boot-starter-web pulls in spring-boot-starter-tomcat transitively, so Tomcat is the out-of-the-box default embedded container.
Switching containers just means adjusting dependencies — exclude the default and add the alternative starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
Auto-configuration detects whichever server's classes are actually present on the classpath and configures that one — no other code changes needed, since Spring Boot's ServletWebServerFactory abstraction (TomcatServletWebServerFactory, JettyServletWebServerFactory, UndertowServletWebServerFactory) hides the container-specific setup behind a common interface.
Configuration happens through the same standard application.properties/application.yml keys regardless of which container is active:
server.port=8443
server.tomcat.max-threads=200
server.compression.enabled=true
Lifecycle: the embedded server starts as part of ApplicationContext refresh and stops as part of context shutdown — there's no separate "deploy" step; running the JAR (java -jar app.jar) both starts the Spring container and the web server together, which is a large part of why Spring Boot applications are simple to containerize (a Docker image just needs a JRE and the JAR).
Choosing between them in practice: Tomcat is the safe, well-understood default for most applications; Undertow is often chosen for a lighter memory footprint and strong non-blocking I/O performance (frequently paired with reactive/WebFlux applications); Jetty is a solid alternative with its own tuning characteristics, sometimes preferred in existing Jetty-based infrastructure.