Now you can stage classes and jars like this:

clojure -A:build -m package

The Dockerfile uses a specific version of the relevant JDK image. Never use fleeting tags like latest for a production build - you want those to be predictable and repeatable:

FROM openjdk:11.0.2-slim ADD target/lib/lib /app/lib ADD target/classes /app/classes WORKDIR /app CMD java $JAVA_OPTS -cp "classes:lib/*" our-app.core

Externalizing JVM parameters with $JAVA_OPTS allows us to tweak runtime characteristics without building a new artifact. Here's an example of setting it from a Kubernetes deployment descriptor to configure JMX and heap memory:

containers : - name : our-service image : our.repo.com/our-app:ae31ade5ba env : - name : JAVA_OPTS value : "-Dcom.sun.management.jmxremote.rmi.port=9090 -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=9090 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.local.only=false -Djava.rmi.server.hostname=localhost -Xms128m -Xmx128m"

We use a Makefile to tie everything together, so we can do things like:

make docker # AOT compiles first if sources have changed

Here's something to get you started:

VERSION := $( shell git rev-parse --short = 10 HEAD ) target : mkdir -p target target/classes/our_app/core.class : deps . edn src /**/* target clojure -A:build -m package build : target / classes / our_app / core . class docker : target / classes / our_app / core . class docker build -t our-app: $( VERSION ) . clean : rm -fr target .PHONY : build docker clean

This is a very straight-forward approach that uses little tooling, has few concepts to understand, no runtime component, and starts quickly.