Deployment of Clojure app to a production with Docker

Clojure deployment to a production itself not a very complicated task, but with the Docker it becomes more flexible solution. You can deploy your app to the Amazon EC2 or even to Amazon EC2 Container Service just in minutes! In this example we have Ring web server listening on 3000 port an some public resources, like bootstrap css and js in resources/public folder, and application-specific settings in resources/settings folder.

Caution: your Ring web server should be configured to listen on 0.0.0.0 address in order to play nicely with docker.

First, assume we will deploy to the production compiled app as an executable uberjar file. So, you should make sure that your application can be compiled with command

lein uberjar 

this will produce the new JAR file in the target folder, like following target/clojure-workshop-0.1.0-SNAPSHOT-standalone.jar.

Check that your executable is running

java -jar target/clojure-workshop-0.1.0-SNAPSHOT-standalone.jar

this should run your Clojure application. Keep in mind, that the entrypoint in your application is specified in project.clj file. In the clojure-workshop.core you should have private “main” function defined.

:main clojure-workshop.core

In case all above is okay, you can proceed with the Dockerfile creation.

Creation of the docker container

FROM java:8-alpine
RUN mkdir -p /app /app/resources
WORKDIR /app
COPY target/uberjar/*-standalone.jar .
COPY resources/public resources/public
CMD java -jar clojure-workshop-0.1.0-SNAPSHOT-standalone.jar
EXPOSE 3000

Then build and tag your image

docker build -t mprokopov/clojure-workshop .

And try to launch

docker run -p 3000:3000 mprokopov/clojure-workshop 

In case you did everything right you can enjoy your app response in browser http://localhost:3000. Keep in mind, that the docker assigns different internal network addresses for running containers, so you should configure your Ring web server to listen on the ‘0.0.0.0’ address.

Deployment to the production

Then your task is to deploy to the production. Let’s say you have a docker repository, for instance, in the Dockerhub. Push your image there.

docker push mprokopov/clojure-workshop

Spin off your virtual server instance and login to your remote server via ssh, ensure you have docker instance running with ‘docker ps’, if not, install docker service. Then we can continue

docker pull mprokopov/clojure-workshop

If you decide to have nice looking docker-composer.yaml, this could be your example how to create one:

services:
srv:
build: .
image: mprokopov/clojure-workshop
volumes:
- ./resources/settings:/app/resources/settings
ports:
- 3000:3000
restart: always

See that settings volume? In this way you can store your sensitive or permanent data outside of the ephemeral container to survive between container spin-offs.

docker-compose up -d 

If you need nginx-proxy before your container you can enjoy jwilder/nginx-proxy container, then your docker-compose should include VIRTUAL_HOST env variable.

services:
srv:
build: .
image: mprokopov/clojure-workshop
environment:
VIRTUAL_HOST=mydomain.com
volumes:
- ./resources/settings:/app/resources/settings
ports:
- 3000:3000
restart: always

After this you can open http://mydomain.com and enjoy your Clojure application in production with nginx-proxy.