In this guide, I describe the available web server options for Clojure and give my recommendations.
Choose Ring-Jetty for small side projects.
The heart of your web application is the web server library. It is what opens the network socket, listens for web requests, and ultimately calls your code. Because Clojure runs on the JVM, it can take advantage of world-class web servers written in Java.
There are two pretty distinct types of projects that have different requirements. The two types of projects are 1) small side projects and 2) production systems for businesses. There may be other kinds of projects, but these two extremes span the range and will let us examine the important characteristics of each server. I don’t need to talk about projects that are specifically about trying out a new technology. For instance, a project to learn how Vert.x might work with Clojure sounds great, but the choice to use Vert.x is obvious. The kinds of projects I’m talking about are practical web servers to serve some purpose.
Side project is the term I’m going to use to describe a class of application. There are different reasons to create a side project. You might create a side project to learn to code, to prototype an idea, or to service a small community. Side projects have a small codebase, a small number of users, and limited time to work on them. It’s not really about where they run. Instead, it’s about the size of the project, the scale of users, and the cost of working on them.
Production systems, on the other hand, is the term I will use for critical business applications. These systems tend to have more and more complex code, a high scale of users, reliability requirements, and more time to spend developing the system. Again, it’s not about where they run. It’s about the size, the scale, and the dependability.
Web server criteria
Let’s discuss the criteria that are important for choosing a web server in the context of these two jobs. These are the characteristics that distinguish the side project’s needs from the production system’s needs.
Ease of getting started
The most important aspect for side projects is that you don’t want the setup to take a long time. You have a great idea for something you’d like to run on the web. You’ve dedicated some free time on the weekend or in the evening to get something working. The last thing you want is to take a few hours just to get your web server started.
This is certainly a factor for production systems, but not nearly as important. If you’re building a production system, you’re going to run it for so long that even spending a day getting it set up will be dwarfed by the rest of development.
Available ecosystem of compatible libraries
Leveraging existing libraries can give you speed. I know first-hand how difficult it can be to write production software on a platform without the libraries you would expect. Likewise, for side projects, you don’t have the development resources to do what libraries should do. The size and activity of the ecosystem are very important.
Ongoing support and development
Projects come and go. Some projects get a lot of love and seem to last forever. Others have a lot of potential but lose funding. You want a web server that will be around for a while. You want someone making sure it still works when things it is built on are upgraded.
Speed and reliability
If you’re running a production system, you’re going to get a lot of users pounding your ports (you hope!). And for your existing users, you want to make sure it doesn’t cave under load. This is not so important for side projects, which have a relatively small number of users.
Do you need WebSocket support? What about Server-Sent Events, or HTTP/2 support? Do you need an asynchronous request model? These types of features may or may not be important to your application. Many successful applications have been written without them, so it’s likely that yours won’t need them, either.
Major categories of options
- Server started by your application
- Application container
- Clojure running inside of web server
There are various web servers that you can run from your software. You choose when to start them and when to stop them. Usually, you pass them some kind of function to handle the requests. These include Undertow, Netty, Jetty, Vert.x, and HTTP Kit (written in Clojure).
There are two major application containers. These are software that you submit your code to, which run them as separate applications. Each application has its own isolated classpath, so they can have different versions of libraries without interfering with each other. The two options are Tomcat and Wildfly.
You can also embed Clojure inside of a web server. There is a library called Nginx-Clojure that supports running Clojure (or Java or Groovy) inside of nginx. You get the benefits of the web server and the benefits of Clojure.
In general, I recommend option #1. However, you might have deployment constraints that will determine your choices.
These are my picks for web servers for applications you would start today.
Side project recommendation
Ring-Jetty (part of Ring) is the best web server for side projects. It is easy to use, doesn’t require any extra dependencies, and has a huge ecosystem of compatible libraries. If you’re interested in getting stuff done in a short time, Jetty will take you very far.
Jetty is not the fastest web server out there, but it is very reliable. The Clojure wrappers are used by many companies with a lot of scale.
Ring-Jetty is the web server that comes with Ring. It is a Clojure wrapper around Jetty. It is perfectly fine and acceptable and has the easiest setup in my opinion. If you are going to use Ring, it’s the best option. In addition, it is well-maintained and has a lot of users.
The biggest downside is that Ring-Jetty does not have convenience wrappers for WebSockets, Server-Sent Events, or HTTP/2. If you would like those, an alternative with a compatible interface is ring-jetty9-adapter.
Production system recommendations
Choosing a system for production is more complicated. The choice of ecosystem or programming model is more important.
If you choose the Ring ecosystem, stick with the Ring-Jetty web server that comes with Ring. It’s reliable and well supported. Ring uses Adapters, Middleware, and Handlers.
Many people choose the Pedestal model of development. It uses Adapters, Interceptors, and Handlers. The Pedestal system is a high-performance web application framework. It includes its own adapters. I recommend the Jetty adapter. It even supports WebSockets and Server-Sent Events.
Yada’s model is to provide a complete and correct HTTP response to a request. Ring and Pedestal models piece together partially correct responses based on small transformations in middleware or interceptors. Yada defaults to Aleph for its web server, which is a high-performance Clojure wrapper for Netty. Zach Tellman seems to be winding down his involvement with Aleph, but it is stable and used by many companies.
Wildfly (with lein-ring)
Wildfly is a high-performance Java Application container supported by RedHat. lein-ring will let you build a WAR file for deployment.
I don’t recommend using Tomcat if you have the choice. It’s old tech and there are much better replacements out there these days.
Give it a pass
HTTP Kit is a web server (and web client) written in mostly Clojure from the ground up. It’s a great idea, the code is supposed to be really clear, and it works great with the ecosystem.
However, because it is a fresh implementation (with no dependencies!), it suffers from a small pool of contributors. It doesn’t get the attention it needs to keep up with bugs and features like the Java-based libraries do. It isn’t as reliable and it isn’t as fast. There’s really no need to use HTTP Kit now. It filled a need in the Clojure world for a while, but that need is now better served by Java-based solutions with good Clojure wrappers.
Immutant is a suite of Clojure libraries for building web applications. Its web server is Undertow, so it is very fast and reliable. It also includes libraries for messaging, caching, scheduling, and transactions. If you are building a large web application, it is likely you will need these other facilities anyway. Consider using Immutant for a cohesive selection of these libraries with nice Clojure wrappers. Undertow and Netty are near the top of the competitive benchmarks (Top 25 across all languages).
Unfortunately, the project to support Immutant has been de-funded. If you’re starting something new, I can’t recommend this one. However, there is a fork afoot from Metosin called Pohjavirta. It’s not ready yet, but it should be interesting when it is released.
Interesting and worth exploring
Nginx-Clojure is a way to run handlers and filters written in Clojure directly embedded in nginx.
It’s hard for me to recommend this one because I have never used it. From the outside, it seems like a bad idea. But the more I thought about it, the more it seemed acceptable.
For instance, in most web server configurations, I would probably want to run nginx in front of it anyway. nginx provides a lot of functionality that is more secure and faster than I could ever hope to do in my own code. It will securely proxy HTTPS to my HTTP server, for example. It can also GZIP responses so I don’t have to worry about it. If I’m going to have nginx anyway, why not run my code directly inside?
It is possible to use the Java libraries directly. I don’t recommend it for beginners, but if you’re into that or you need a feature not exposed by your web development model, go for it. Undertow, Vert.x, Netty, and Jetty are all great options.
Keep an eye on this one
Pohjavirta is a high-performance Clojure wrapper for Undertow, developed by Metosin, the same folks who created Reitit (web router). Since it is in pre-release, you shouldn’t use it yet. But it may prove to be the fastest wrapper out there.