How to Deploy Clojure to Docker Without Writing a Dockerfile

M
Miget Team
Writer
January 22, 2026
4 min read

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=1 for 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:

  1. Detect Clojure from project.clj or deps.edn
  2. Read versions from project files
  3. Run lein deps with caching
  4. Build an uberjar with lein uberjar
  5. Output a slim JRE-based production image

How Are Versions Detected?

Clojure Version

SourceExample
.clojure-version1.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"}
DefaultLatest stable

Leiningen Version

SourceExample
project.clj (:min-lein-version):min-lein-version "2.11.2"
Default2.11.2

Java Version

SourceExample
system.propertiesjava.runtime.version=21
Default21

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?

VariableValue
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

Stay UpdatedWith Latest Posts

Subscribe to our newsletter and never miss a new blog post, update, or special offer from the Miget team.