
Wie man Spring Boot dockerisiert und deployed
Lukas MauserDieses Dockerfile kompiliert deine Spring Boot App mit dem Maven Wrapper zu einem lauffähigen Jar und lässt es dann auf einem kleinen JRE-Image laufen. Durch den Multi-Stage Build landen das JDK und der Build-Cache nie im finalen Image.
Falls du nur zum Kopieren und Einfügen hier bist, hier ist das fertige Dockerfile, das ein Image für deine Spring Boot App erstellt:
FROM eclipse-temurin:21-jdk-alpine AS build
WORKDIR /app
# Erst den Maven Wrapper und die pom kopieren, damit die Dependencies gecacht werden
COPY .mvn/ .mvn/
COPY mvnw pom.xml ./
RUN ./mvnw dependency:go-offline -B
# Den Quellcode kopieren und das Jar bauen
COPY src ./src
RUN ./mvnw clean package -DskipTests
FROM eclipse-temurin:21-jre-alpine AS runtime
WORKDIR /app
# Nur das gebaute Jar aus der Build-Stage kopieren
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]
Und hier ist die .dockerignore Datei:
target
.git
.idea
*.iml
Um das Image zu bauen und auszuführen, nutze diese Befehle:
docker build -t spring-app .
docker run -p 8080:8080 spring-app
Nicht nur zum Kopieren und Einfügen hier? Dann lass uns durchgehen, was im Dockerfile passiert!
Das Setup
Für dieses Tutorial gehe ich davon aus, dass du ein Spring Boot Projekt hast, das Maven und den Maven Wrapper nutzt (das mvnw Script und den .mvn Ordner, den Spring Initializr generiert). Wenn du Gradle nutzt, ist die Idee identisch, du tauschst nur den Wrapper und die Build-Befehle gegen ./gradlew bootJar.
Ein Spring Boot Jar ist self-contained. Es bündelt einen eingebetteten Webserver, sodass du nach dem Bauen nur noch eine Java-Runtime brauchst, um es zu starten. Deshalb kann die Runtime-Stage so klein sein.
Das Dockerfile
Die build Stage kompiliert die App:
- Basis-Image:
- Nutzt
eclipse-temurin:21-jdk-alpine, ein komplettes JDK auf Alpine, weil das Kompilieren den Compiler braucht.
- Dependency-Caching:
- Kopiert erst den Maven Wrapper und die
pom.xmlund führt danndependency:go-offlineaus. Das lädt alle Dependencies in einen Layer, den Docker cacht, sodass eine Code-Änderung nicht bei jedem Build einen kompletten Re-Download auslöst.
- Build-Schritt:
- Kopiert den Quellcode und führt
mvnw clean package -DskipTestsaus. Das erzeugt ein lauffähiges Jar imtargetOrdner. Die Tests werden hier absichtlich übersprungen, da du sie normalerweise in der CI ausführst, bevor du das Image baust.
Die runtime Stage bleibt schlank:
- Runtime-Image:
- Nutzt
eclipse-temurin:21-jre-alpine, das nur die Java-Runtime enthält, nicht den Compiler und das Tooling. - Kopiert nur das Jar aus der Build-Stage mit einem Wildcard, sodass du die Version nicht im Dateinamen hardcoden musst.
- Exponiert Port 8080, den Standard von Spring Boot, und startet die App mit
java -jar.
Konfiguration wie deine Datenbank-URL sollte aus Environment-Variablen kommen. Spring Boot mappt sie automatisch auf Properties, sodass SPRING_DATASOURCE_URL ganz ohne Extra-Aufwand spring.datasource.url setzt.
Deployment
Du kannst diesen Docker Container bei jedem Cloud-Anbieter deployen, der Docker unterstützt. Du könntest zum Beispiel Plattformen wie Heroku, DigitalOcean oder AWS ECS nutzen. Da ich Mitgründer von Sliplane bin, zeig ich dir, wie du es dort deployst.
Nach der Anmeldung kannst du einen neuen Service erstellen, indem du dein GitHub Repository auswählst. Behalt dann die Standardeinstellungen bei, füg die Environment-Variablen hinzu, die deine App braucht, und klick auf Deploy.

Nach dem Deployment ist deine Spring Boot App unter einer Subdomain von sliplane.app verfügbar, normalerweise ist das einfach dein Service-Name.
Du kannst auch die Logs deiner App sehen, Metriken wie CPU- und Speichernutzung einsehen, persistenten Speicher hinzufügen und vieles mehr. Wann immer du in dein Repository pushst, deployed Sliplane deine App automatisch.
Wenn du Sliplane ausprobieren möchtest, sind die ersten 2 Tage kostenlos! Probier es aus und lass mich wissen, was du davon hältst :)
Nächste Schritte
Für größere Apps kannst du die Rebuild-Zeiten mit Spring Boots Layered Jars weiter verkürzen, die Dependencies und deine eigenen Klassen in separate Docker-Layer aufteilen. Brauchst du Hilfe beim Einrichten davon oder beim Anbinden einer Managed-Datenbank? Melde dich gerne!
Bis dann,
Lukas