Spring Boot projects ship with Tomcat handling HTTP requests out of the box. Most developers never give the servlet container a second thought. But when your team needs Jetty's smaller footprint, better support for long-lived connections, or compatibility with specific libraries, swapping servers is easier than you might expect.
The process comes down to two dependency tweaks. Exclude Tomcat, add Jetty, rebuild. That's the entire migration. Your application code stays the same.
What happened:
- Strip the Tomcat starter from your web dependencies
- Pull in the Jetty starter instead
- Spring Boot detects the change automatically
- No controller or service code requires updates
When Jetty Makes Sense
Tomcat dominates because it works well for most applications. It starts fast, patches quickly, and plays nicely with Spring Boot's defaults. Jetty earns consideration when your workload demands something different.
Teams building real-time features often prefer Jetty. Its continuation model handles many persistent connections without consuming proportionally many threads. That's a meaningful advantage for chat applications, live dashboards, or streaming services. Some organizations have standardized on Jetty for operational or compliance reasons.
For standard REST APIs returning JSON payloads, the performance gap is negligible. Your choice matters when connection duration or memory efficiency becomes a bottleneck.
Updating Maven Dependencies
Locate your pom.xml and find the spring-boot-starter-web entry. Wrap it with an exclusions block targeting the Tomcat starter, then append the Jetty starter dependency. The exclusion goes inside the dependency declaration, and a fresh dependency entry brings in Jetty.
After saving, run your build. Maven pulls the Tomcat JARs out of your classpath and drops Jetty's in their place.
Updating Gradle Dependencies
Gradle's Groovy DSL follows a similar pattern. You exclude the Tomcat module from the web starter and declare the Jetty dependency on the next line.
Sync your project and watch Gradle resolve the new dependency tree.
What Your Build Contains Now
Inspect the compiled artifacts. The Tomcat embed JARs vanish. In their place, your project now bundles Jetty's servlet stack: the HTTP layer, webapp support, security filters, and server core. Package size shifts are minimal in most cases.
Confirming the Switch Works
Launch your application and watch the startup output. The log messages reveal which server is running. Jetty prints its version string and context initialization details. If you still see Tomcat references, your exclusion missed a transitive dependency somewhere.
The reference output shows Jetty initializing logging at a fixed millisecond interval, starting the embedded context, and finally confirming startup on the configured port. These markers tell you everything is running through Jetty rather than Tomcat.
Fine-Tuning Through Configuration
Spring Boot exposes Jetty-specific knobs through your YAML or properties file. Thread counts, connection timeouts, and access logging all have dedicated settings.
“`yaml
Server:
Jetty:
Threads:
Min: 8
Max: 200
Accesslog:
Enabled: true
Retention-period: 31
“`
These settings are optional. The defaults handle typical workloads without adjustment. The thread minimum and maximum directly control how your server handles concurrent load. The access log toggle gives you request visibility in environments where you need it.
Our Analysis: Risk and Reward
The beauty of this switch lies in Spring Boot's abstraction. Your business logic lives in controllers and services that never call Tomcat or Jetty APIs directly. The container is infrastructure, not code.
This means low migration risk. You can experiment, measure, and revert in minutes. If Jetty causes problems, removing the two dependency changes restores Tomcat instantly.
The real beneficiaries are specialized applications. Standard REST microservices rarely gain measurable value from the swap. But when your application maintains persistent connections or targets embedded deployments, Jetty's characteristics justify the change.
Watch for dependency conflicts. Some Spring Boot starters bundle Tomcat assumptions. ClassNotFoundException errors after the switch usually mean something is still pulling Tomcat in transitively.
Practical Example: Real-Time Notification Service
Imagine you're building a service that pushes alerts to many mobile devices simultaneously. Each device maintains an open connection waiting for updates.
With Tomcat's synchronous model, idle connections consume threads. A service handling hundreds of devices might need hundreds of threads, eating memory even when devices sit idle. Jetty's asynchronous handling shares those connections across a smaller thread pool.
Configure your service conservatively with reasonable defaults:
“`yaml
Server:
Jetty:
Threads:
Min: 8
Max: 200
Connection-idle-timeout: 60000
“`
Deploy, monitor memory usage under load, and adjust the max thread count accordingly. The configuration controls your resource footprint. You can scale the thread pool up if you see request queuing, or down if memory becomes constrained.
What to Watch Next
After making the switch, run your complete test suite. Update any test slices that assume Tomcat is present. Monitor your first few production deployments closely. Most configuration issues surface immediately in startup logs.
Bottom Line
The Tomcat-to-Jetty migration requires only dependency changes. No code rewrites, no controller updates, no service layer modifications. Revert by restoring the original dependencies if you hit issues. If your project needs Jetty's characteristics, the path forward is clear.
Key takeaways
- The switch requires only dependency changes, no code rewrites
- Build output reflects the new container automatically
- Check startup logs to confirm Jetty is running
- Performance gains apply mainly to persistent-connection workloads
- Revert by restoring the original dependencies if needed
Frequently asked questions
Does Spring Security work with Jetty?
Yes. Spring Security integrates regardless of which servlet container runs underneath.
Can I switch back to Tomcat later?
Yes. Remove the Jetty dependency and restore the exclusion, then rebuild.
Are there performance differences between the two?
For typical REST APIs, the difference is negligible. Jetty often wins for applications with many long-lived connections.
Do I need to update my controllers?
No. Your application code remains identical.
Where are all the Jetty configuration options documented?
Spring Boot's official documentation covers the server.jetty namespace completely.