I've been running Solon apps in production for a while now, and one thing that keeps surprising people is how the server engine works.
Not "which server does Solon use" — because the answer is: you choose.
Solon doesn't bundle a hardcoded HTTP server. The framework core is about 0.3MB. The HTTP engine is a separate plugin. You pick one. Or two. Or swap them between environments without touching a single line of application code.
Here's a quick rundown of the 10 HTTP server options, what they're good for, and how to switch.
The One-Dependency Swap
In Solon, changing your HTTP server means changing one dependency in pom.xml. Nothing else.
<!-- Default: jdkhttp (built-in JDK, 0.3MB) -->
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-web</artifactId>
</dependency>
Enter fullscreen mode Exit fullscreen mode
Want Undertow instead?
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.noear</groupId>
<artifactId>solon-server-jdkhttp</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-server-undertow</artifactId>
</dependency>
Enter fullscreen mode Exit fullscreen mode
That's it. Your @Controller, @Mapping("/hello"), filters, interceptors — everything stays the same. The routing layer is framework-owned; the server underneath is a pluggable adapter.
The 10 Engines at a Glance
| Plugin | Size | Protocols | Reactive | JDK |
|---|---|---|---|---|
| jdkhttp | 0.3MB | http | ✅ | 8+ |
| smarthttp | 0.8MB | http, ws | ✅ | 8+ |
| grizzly | 1.8MB | http, ws, http2 | ✅ | 8+ |
| vertx | 6.3MB | http, ws, http2 | ✅ | 8+ |
| jetty | 2.7MB | http, ws | ✅ | 8+ |
| undertow | 4.6MB | http, ws, http2 | ✅ | 8+ |
| tomcat | — | http, ws, http2 | ✅ | 8+ |
| jetty-jakarta | 3.9MB | http, ws, http2 | ✅ | 17+ |
| undertow-jakarta | — | http, ws, http2 | ✅ | 17+ |
| tomcat-jakarta | — | http, ws, http2 | ✅ | 17+ |
Plus two dedicated WebSocket-only servers (solon-server-websocket at 0.4MB, solon-server-websocket-netty at 3.6MB) and one Socket.D server.
How to Choose
For microservices and edge services: jdkhttp or smarthttp
If you're building a lightweight API service or a microservice that needs to start in under a second, stick with jdkhttp. It's 0.3MB, requires zero external dependencies, and supports reactive I/O. The JDK built-in com.sun.net.httpserver is perfectly adequate for most API workloads.
smarthttp (0.8MB, Chinese-contributed) adds WebSocket support on the same port — useful if you need both REST and WebSocket without adding a second server.
@SolonMain
public class App {
public static void main(String[] args) {
Solon.start(App.class, args, app -> {
app.enableWebSocket(true);
});
}
}
Enter fullscreen mode Exit fullscreen mode
When you need HTTP/2: grizzly, undertow, vertx, or tomcat
All four support HTTP/2. Enable it with a single line:
app.onEvent(HttpServerConfigure.class, e -> {
e.enableHttp2(true); // v2.3.8+
});
Enter fullscreen mode Exit fullscreen mode
Undertow is a solid middle ground — 4.6MB, Apache 2.0 license, good community adoption. Vert.x is heavier (6.3MB) but gives you the full Vert.x event-loop ecosystem if you're already using it.
Traditional Java shops: Jetty or Tomcat
If your team is migrating from Spring Boot + embedded Tomcat, solon-server-tomcat is the most natural drop-in. It uses the same Tomcat v9 engine underneath — your ops team's existing tuning knowledge carries over.
Jetty is a great alternative if you prefer Eclipse ecosystem. It also has explicit JSP support via solon-server-jetty-add-jsp.
Jakarta EE migration: jetty-jakarta, undertow-jakarta, tomcat-jakarta
If you're on JDK 17+ and targeting Jakarta namespace (jakarta.*), use the Jakarta variants. They're paired with the corresponding Jakarta server versions (Jetty 12, Undertow 2.3, Tomcat 11).
The Code That Never Changes
This is the part I like most. No matter which server engine you pick, your application code looks identical:
@Controller
public class DemoController {
@Mapping("/hello")
public String hello() {
return "Hello world!";
}
@Mapping("/greet/{name}")
public String greet(String name) {
return "Hello, " + name + "!";
}
}
Enter fullscreen mode Exit fullscreen mode
The same code runs on jdkhttp (0.3MB, no dependencies), Undertow (4.6MB, HTTP/2), or Tomcat (production-hardened). Zero changes.
Advanced: Multiple Ports, SSL, and Debug Mode
All server adapters share the same configuration surface:
# app.yml
server.port: 8080
server.http.port: 8080 # alias
server.ssl.keyStore: "/data/ca/demo.jks"
server.ssl.keyPassword: "demo"
Enter fullscreen mode Exit fullscreen mode
Or programmatically:
Solon.start(App.class, args, app -> {
// Add a second HTTP port (useful when main port is HTTPS)
app.onEvent(HttpServerConfigure.class, e -> {
e.addHttpPort(8082);
e.enableDebug(true); // debug mode for smarthttp/jdkhttp
});
});
Enter fullscreen mode Exit fullscreen mode
Custom SSLContext (v2.5.9+):
app.onEvent(HttpServerConfigure.class, e -> {
SSLContext sslContext = buildMyCustomSSLContext();
e.enableSsl(true, sslContext);
});
Enter fullscreen mode Exit fullscreen mode
What About the "Default" Default?
If you add solon-web without any explicit server exclusion, Solon uses jdkhttp — the built-in JDK HTTP server. It's the lightest option (0.3MB), requires zero external JARs, and works for 90% of development scenarios.
In production, you might want something more tuned. But the beauty is: you don't decide on day one. Start with jdkhttp, benchmark, then swap to Undertow or Tomcat when you need HTTP/2 or WebSocket — all without rewriting a single route.
Honest Limitations
-
Tomcat (v9) doesn't support WebSocket directly — you need the
solon-server-tomcat-add-websocketsub-plugin. - smarthttp had a file upload size cap (~2.1GB) before v3.0.3 — fixed now, but worth noting if you're on an older version.
- Vert.x is the heaviest at 6.3MB — great for reactive workloads but overkill for a simple CRUD API.
- Jakarta variants (jetty-jakarta, undertow-jakarta, tomcat-jakarta) require JDK 17+.
Summary
Solon's pluggable server architecture is one of those features that seems small on paper but makes a big difference in practice. One dependency change, zero code impact, and a full spectrum of engines from 0.3MB to full-featured production servers.
| Need | Pick |
|---|---|
| Lightest API server | jdkhttp (0.3MB) |
| REST + WebSocket on same port | smarthttp (0.8MB) |
| HTTP/2 + solid performance | undertow (4.6MB) |
| Spring Boot migration | tomcat |
| Reactive event-loop | vert.x or grizzly |
| Jakarta EE (JDK 17+) | jetty-jakarta / undertow-jakarta |
| Standalone WebSocket | solon-server-websocket (0.4MB) |
The server is infrastructure. The framework is your code. Solon keeps them separate — and that's a good thing.
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.