Tomcat is like a kitchen built into your house: Spring Boot brings the server with the app, so you do not have to install one separately.
Question: Explain embedded Tomcat.
Answer: In Spring Boot, embedded Tomcat means the Tomcat web server runs inside your application process instead of being installed and managed as a separate server. When you add spring-boot-starter-web, Boot usually auto-configures Tomcat, starts it on startup, and listens on port 8080 by default. That is why a Boot app can be packaged as a simple runnable JAR.
Interview-Ready Answer: 'Embedded Tomcat means my Spring Boot app starts its own Tomcat server inside the same JVM, so I can run the app as a self-contained JAR. Spring Boot auto-configures it when I use spring-boot-starter-web, typically on port 8080 unless I change it. The big benefit is simpler deployment and fewer server setup steps.'
Tomcat is a servlet container (a server that runs Java web requests through the Servlet API). In Spring Boot, embedded means that container is started by your app, inside the same JVM process, instead of being installed separately and managed as a separate server. Your application becomes both the business logic and the web server.
spring-boot-starter-web. That pulls in Spring MVC, the Servlet API, and Tomcat libraries by default.TomcatServletWebServerFactory bean. This factory knows how to build and configure Tomcat for the app.TomcatWebServer from that factory and binds it to a port, usually 8080 unless you set server.port or customize it in code.DispatcherServlet (the main Spring MVC front controller) with Tomcat. A front controller is one entry point that receives all requests and routes them to controllers.It makes local development and deployment much simpler. You can ship one executable JAR, run it with java -jar, and the same artifact works in a laptop, a VM, or a container. It also reduces environment mismatch: the app carries the server version it was tested against.
| Embedded Tomcat | External Tomcat |
|---|---|
| Server ships with app | Server installed separately |
| Run as JAR | Deploy WAR |
| Boot controls startup | Ops controls server |
| Good for containers | Good for shared app servers |
| Fewer moving parts | More server admin work |
Spring Boot 3 uses Tomcat 10.1 and the Jakarta Servlet API, so package names moved from javax.servlet to jakarta.servlet. Spring Boot 2 used Tomcat 9 and the older javax namespace. The default port is 8080, but you can change it with server.port or programmatically via the embedded server factory.
The container itself adds only a small startup and memory cost compared with your application code, but Tomcat still uses a thread pool. Common Tomcat defaults are around 200 max request threads and an accept backlog near 100. That means a busy app can queue briefly and then start rejecting or timing out if traffic exceeds the pool and backlog. Request routing is effectively O(1) at the server level; the expensive part is your application logic, database calls, and serialization.
8080 is already in use, startup fails fast with a bind error.Memory hook: think of embedded Tomcat as a suitcase hotel: the app checks in with its own front desk, instead of renting a separate building across town.
Real-world story: A checkout service in a Kubernetes cluster runs as a Spring Boot JAR with embedded Tomcat. Each pod owns its own HTTP server, so the team can scale pods horizontally without asking ops for a shared Tomcat installation. One Friday, a release changed the app to use a fixed port that was already taken by a sidecar container. The pod kept crashing, and logs showed java.net.BindException: Address already in use. Users saw 502 errors at the load balancer, health checks failed, and the deployment never became ready. The lesson: with embedded Tomcat, the web server is part of the app’s startup path, so server configuration mistakes become application outages, not just server chores.
package com.example.embeddedtomcat;
import java.util.Map;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.event.EventListener;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
@SpringBootApplication
public class EmbeddedTomcatDemoApplication {
public static void main(String[] args) {
SpringApplication.run(EmbeddedTomcatDemoApplication.class, args);
}
// Port 0 asks the OS for a free port, so the sample starts reliably
// even if 8080 is already busy on the machine.
@org.springframework.context.annotation.Bean
WebServerFactoryCustomizer<TomcatServletWebServerFactory> embeddedTomcatCustomizer() {
return factory -> {
factory.setPort(0);
factory.setContextPath("/demo");
};
}
@EventListener
public void logActualPort(WebServerInitializedEvent event) {
// Embedded Tomcat lives inside the same JVM, so we can inspect the live port after startup.
System.out.println("Embedded Tomcat started on port: " + event.getWebServer().getPort());
}
@RestController
@RequestMapping("/api")
static class GreetingController {
@GetMapping("/ping")
public Map<String, Object> ping() {
return Map.of(
"status", "ok",
"container", "embedded Tomcat"
);
}
@GetMapping("/greet")
public ResponseEntity<Map<String, Object>> greet(@RequestParam(required = false) String name) {
// This edge case shows that application errors are still handled normally
// even though the HTTP server is embedded.
if (name == null || name.isBlank()) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "name is required");
}
return ResponseEntity.ok(Map.of(
"message", "Hello, " + name,
"server", "embedded Tomcat",
"path", "/demo/api/greet"
));
}
}
}Follow-up & Tricky Questions:
spring-boot-starter-web is on the classpath, Boot auto-configures a servlet web server, and Tomcat is the default embedded choice unless you replace it.server.port and server.servlet.context-path, or customize the embedded server factory in code.java -jar, while a WAR is packaged for deployment into an external servlet container.jakarta.servlet; Boot 2 used javax.servlet.Common Mistakes:
javax to jakarta. Correction: Boot 3 and Tomcat 10 use Jakarta namespaces, which breaks older imports.Memory Hook: Your app brings its own front desk. Embedded Tomcat means the server travels with the application instead of living in a separate building.
Cheat Sheet:
spring-boot-starter-web.8080 unless changed.TomcatServletWebServerFactory.jakarta.servlet.Practice Tasks:
spring-boot-starter-web and confirm Tomcat starts in the logs.server.port and server.servlet.context-path, then hit the endpoint again.