How to Deploy Clojure to Docker Without Writing a Dockerfile
You can containerize any Clojure application without writing a Dockerfile using migetpacks. It auto-detects your project from project.clj or deps.edn, reads Clojure and Leiningen versions, builds an uberjar, and produces an optimized Eclipse Temurin JRE image. One command, zero configuration.
Why Is Writing a Dockerfile for Clojure So Complicated?
A production-ready Clojure Dockerfile requires:
- Multi-stage builds to separate Leiningen from runtime
- JDK version selection compatible with your Clojure version
- Proper
LEIN_ROOT=1for non-interactive builds - Uberjar configuration and naming
- Maven/Ivy dependency caching
- JVM memory settings for production
The Clojure/Leiningen toolchain with its JVM requirements makes Docker configuration verbose.
How Do I Containerize a Clojure App Without a Dockerfile?
Open your terminal, navigate to your Clojure project, and run:
cd ~/projects/my-clojure-api
docker run --rm \
-v "$PWD":/workspace/source \
-v /var/run/docker.sock:/var/run/docker.sock \
-e OUTPUT_IMAGE=my-clojure-api:latest \
miget/migetpacks:latest
That's it. migetpacks will:
- Detect Clojure from
project.cljordeps.edn - Read versions from project files
- Run
lein depswith caching - Build an uberjar with
lein uberjar - Output a slim JRE-based production image
How Are Versions Detected?
Clojure Version
| Source | Example |
|---|---|
.clojure-version | 1.11.1 |
project.clj (org.clojure/clojure dependency) | [org.clojure/clojure "1.11.1"] |
deps.edn (org.clojure/clojure) | org.clojure/clojure {:mvn/version "1.11.1"} |
| Default | Latest stable |
Leiningen Version
| Source | Example |
|---|---|
project.clj (:min-lein-version) | :min-lein-version "2.11.2" |
| Default | 2.11.2 |
Java Version
| Source | Example |
|---|---|
system.properties | java.runtime.version=21 |
| Default | 21 |
What Does the Generated Dockerfile Look Like?
# Build stage
FROM clojure:temurin-21-lein AS builder
WORKDIR /build
ENV JAVA_TOOL_OPTIONS="-Dfile.encoding=UTF-8"
ENV LEIN_ROOT=1
COPY project.clj ./
RUN lein deps
COPY . .
RUN lein uberjar \
&& rm -rf src/ test/ dev/ resources/public/js/compiled
# Runtime stage
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=builder --chown=1000:1000 /build /app
You never write or maintain this file - migetpacks handles it automatically.
How Does Uberjar Detection Work?
migetpacks detects custom uberjar names from project.clj:
(defproject myapp "1.0.0"
:uberjar-name "myapp-standalone.jar"
...)
If :uberjar-name is specified, migetpacks uses it when configuring the run command.
What Run Command Is Used?
The default run command:
java -jar target/uberjar/*-standalone.jar
Override with RUN_COMMAND or a Procfile:
web: java -jar target/uberjar/myapp-standalone.jar
What JVM Configuration Is Applied?
| Variable | Value |
|---|---|
JAVA_OPTS | -Dfile.encoding=UTF-8 -XX:MaxRAMPercentage=80.0 -Dclojure.main.report=stderr |
JAVA_TOOL_OPTIONS | -Dfile.encoding=UTF-8 |
How Do I Build Secure Distroless Images?
Enable Docker Hardened Images for minimal containers:
docker run --rm \
-v "$PWD":/workspace/source \
-v /var/run/docker.sock:/var/run/docker.sock \
-e OUTPUT_IMAGE=my-app:latest \
-e USE_DHI=true \
miget/migetpacks:latest
Clojure uses a hybrid approach: the official Clojure build image for building, and DHI distroless Eclipse Temurin for runtime.
How Do I Use Custom JVM Options?
Pass JVM options at build time:
docker run --rm \
-v "$PWD":/workspace/source \
-v /var/run/docker.sock:/var/run/docker.sock \
-e OUTPUT_IMAGE=my-clojure-app:latest \
-e JAVA_OPTS="-Xmx1g -Dclojure.main.report=stderr" \
miget/migetpacks:latest
Next Steps
- Full Clojure documentation - version detection, Leiningen, DHI
- Deploy on Miget - unlimited apps, one price
- Star on GitHub - migetpacks is open source
- Related guides: Java, Kotlin, Scala