JVM Deployment Options

If you're lucky, there comes a time in your Clojure app's life when it will need to leave the comforts of the development environment and to open its ports to the world. As a mature platform, the JVM has a lot of server deployment options. While options, in general, are good, they are intimidating. Some of the deployment options are of the "multimillion-dollar enterprise certification" variety.

When you're working at a company, you usually inherit their deployment system. And that's great because then you just do what they do. But what if you are on your own? What are the options for deploying a Clojure server?

Executables

Deployment options fall into two broad categories. The first is the simplest: the application runs as an executable, just like other servers you're familiar with. You can use all of the operating system stuff you're used to, like turning it into a service that starts at boot.

This is how I run most of my apps. I compile an executable uberjar and use something like supervisord to keep them running.

Containers

The second option is a little more complex. You can actually deploy your application to what's called a container. For instance, Wildfly is an application server container. Tomcat is a servlet container.

Containers do two things that make them important.

  1. They run multiple applications in a single JVM.

The container isolates the different applications so they can load different versions of the same class and can't access each other's memory. You can write your application as if it were running in its own JVM, without the downsides of requiring a lot of memory per application.

  1. They provide services such as web servers, database access, queues, and schedulers.

Your applications can talk to each other using the queues, share database connections, and tap into high-performance web servers.

Now I'll go over some options and how they fit into this scheme. This is a non-exhaustive list of my recommendations.

Heroku

Heroku works out of the box with Clojure. It can detect if you're using Leiningen. It will build an uberjar and deploy it to their cloud as an executable. That means you'll want a built-in web server like Jetty, Netty, http-kit, or aleph. There's a Boot buildpack.

Heroku has a free tier and is affordable from there. You can also get other services like queues, databases, email sending, etc. Deploying to Heroku is as easy as git push heroku master. It's very easy to get something running.

AWS Elastic Beanstalk

Amazon Web Services provides a container service called Elastic Beanstalk. You upload a WAR (Web Application Archive)---basically a JAR file with deployment information in it---and AWS will deploy it to Tomcat for you. With Elastic Beanstalk, you only pay for the AWS resources you use. You're running on EC2 and you can connect to the other services they provide.

A few years ago I used lein-elastic-beanstalk but it looks a little out of date now. You might want to try the AWS CLI.

Hosting on a server

You can run your own server. This gives you complete control at the cost of having to maintain a server. Servers are not that easy! There are security concerns all the time.

I've had good luck running on Ubuntu using supervisord for executable applications. People often use systemd. I've also used Wildfly as an application server.

Microservices

If you're going to be developing a microservices application, you probably want an application container. JVMs take a lot of memory each. Being able to share that across services is important. Also, containers often have a messaging system that you can use out of the box.

Immutant

Immutant is a full-fledge, enterprise grade application server container for Clojure apps. It leverages Wildfly and provides Clojure wrappers for a host of services offered directly in the container. I highly recommend it. The setup is easy and the developers are very responsive.

Docker

You can run Clojure apps in Docker containers. It works, and certainly if you've only got a small number of JVM apps, this may be a great option. But I'm a little worried about memory usage when you start deploying many dockerized JVMs. Here is a good description of the setup for a web app. There are Docker images for lots of Clojure systems, including Immutant.

OSv

I've never run OSv, but it could be an interesting option if you're willing to go bleeding edge. OSv is a tiny OS that can run JVM applications. OSv runs directly on the hypervisor, so it removes a huge layer of abstraction, meaning it's leaner and faster than running a JVM on top of Linux.

There are some instructions. Also here and here.

Other resources

Luminus has deployment docs with many options.

Conclusions

The many deployment options can be overwhelming, but really the abundant choices are a blessing. The JVM is a mature platform that finds itself in a wide variety of situations, from small hobby projects to billion-dol lar enterprise applications.

I've seen a lot of people turned off of Clojure because of the JVM, and I think that's a real shame. The JVM does have its warts, and it has a history, but on the whole it's a great platform. That's why I created JVM Fundamentals for Clojure. Please check it out if you're considering avoiding Clojure because of the JVM.

One of the cool things about the JVM is the Just-in-Time compiler (JIT). It's a way to optimize code at runtime based on the abundant information that running code gives you. We'll look at it next time!