Dockerfile for Spring Boot on OpenShift
Learn how to write production-ready Dockerfiles for Spring Boot applications running on OpenShift. Explore multi-stage builds, image optimization, security best practices, health checks, and enterprise deployment strategies.
Introduction
A Dockerfile defines how a container image is built. It contains a sequence of instructions that package your Spring Boot application along with the required runtime environment.
When deploying applications to OpenShift, a well-designed Dockerfile is critical because it affects:
- Image size
- Startup time
- Security
- Build speed
- Deployment speed
- Resource utilization
In this article, you'll learn how to create production-ready Dockerfiles optimized for OpenShift.
Learning Objectives
By the end of this article, you will understand:
- What is a Dockerfile?
- Docker build lifecycle
- Spring Boot Dockerfile
- Multi-stage builds
- Layer caching
- Image optimization
- OpenShift security requirements
- Best practices
- Enterprise examples
What is a Dockerfile?
A Dockerfile is a text file containing instructions to build a container image.
The Docker Engine reads each instruction and creates image layers.
flowchart LR
DEV["Developer"]
DF["Dockerfile"]
BUILD["Docker Build"]
IMAGE["Container Image"]
REG["Container Registry"]
subgraph OCP["OpenShift Cluster"]
DEPLOY["Deployment"]
POD["Running Pod"]
end
DEV --> DF
DF --> BUILD
BUILD --> IMAGE
IMAGE --> REG
REG --> DEPLOY
DEPLOY --> POD
Docker Build Lifecycle
flowchart LR
DEV["Developer"]
CODE["Source Code"]
MAVEN["Maven Build"]
JAR["Application JAR"]
DOCKER["Docker Build"]
IMAGE["Container Image"]
QUAY["Quay / Image Registry"]
subgraph OCP["OpenShift Cluster"]
DEPLOY["Deployment"]
POD["Spring Boot Pod"]
end
DEV --> CODE
CODE --> MAVEN
MAVEN --> JAR
JAR --> DOCKER
DOCKER --> IMAGE
IMAGE --> QUAY
QUAY --> DEPLOY
DEPLOY --> POD
Sample Project Structure
springboot-demo/
├── src/
├── target/
│ └── app.jar
├── Dockerfile
├── pom.xml
└── deployment.yaml
Basic Dockerfile
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","app.jar"]
Dockerfile Explanation
FROM
FROM eclipse-temurin:21-jre
Defines the base image.
This image already contains:
- Linux
- Java Runtime
- JVM
WORKDIR
WORKDIR /app
Sets the working directory inside the container.
COPY
COPY target/*.jar app.jar
Copies the Spring Boot JAR into the image.
EXPOSE
EXPOSE 8080
Documents the application's listening port.
ENTRYPOINT
ENTRYPOINT ["java","-jar","app.jar"]
Starts the Spring Boot application.
Docker Image Layers
Docker creates immutable layers.
flowchart TD
BASE["Layer 1: Base OS Image"]
RUNTIME["Layer 2: Java Runtime"]
APP["Layer 3: Spring Boot JAR"]
ENTRY["Layer 4: ENTRYPOINT / Startup"]
BASE --> RUNTIME
RUNTIME --> APP
APP --> ENTRY
Only changed layers are rebuilt.
Build Docker Image
docker build -t springboot-demo:1.0 .
Verify:
docker images
Run Container
docker run -p 8080:8080 springboot-demo:1.0
Open:
http://localhost:8080/api/hello
Multi-Stage Build
Instead of packaging the JAR outside Docker, Docker can build everything.
FROM maven:3.9-eclipse-temurin-21 AS builder
WORKDIR /workspace
COPY . .
RUN mvn clean package -DskipTests
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=builder /workspace/target/*.jar app.jar
ENTRYPOINT ["java","-jar","app.jar"]
Multi-Stage Build Architecture
flowchart LR
SRC["Source Code"]
BUILDER["Builder Stage (Maven)"]
ARTIFACT["Spring Boot JAR"]
RUNTIME["Runtime Image (JRE Only)"]
REGISTRY["Container Registry"]
OCP["OpenShift"]
SRC --> BUILDER
BUILDER --> ARTIFACT
ARTIFACT --> RUNTIME
RUNTIME --> REGISTRY
REGISTRY --> OCP
Advantages:
- Smaller images
- Cleaner runtime
- Better security
Layer Caching
Docker caches image layers.
flowchart TD
L1["Layer 1: FROM eclipse-temurin:21-jre"]
L2["Layer 2: Install Dependencies"]
L3["Layer 3: Copy Source Code"]
L4["Layer 4: Maven Build"]
L5["Layer 5: Final Image"]
L1 --> L2
L2 --> L3
L3 --> L4
L4 --> L5
Changing source code rebuilds only the affected layers.
Image Optimization
Large images slow deployments.
Instead of:
1.2 GB
Aim for:
250 MB
or smaller.
Use Minimal Base Images
Recommended:
- eclipse-temurin
- ubi9/openjdk
- Red Hat UBI
Avoid unnecessary packages.
Environment Variables
ENV JAVA_OPTS="-Xms256m -Xmx512m"
Access inside Spring Boot:
server.port=8080
OpenShift Security
OpenShift runs containers as non-root.
Avoid:
USER root
Prefer:
USER 1001
or let OpenShift assign a random UID.
Readiness and Liveness
Enable Spring Boot Actuator.
management.endpoints.web.exposure.include=health
Deployment:
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
OpenShift Deployment Flow
flowchart LR
DEV["Developer"]
GIT["Git Repository"]
BUILD["CI/CD Pipeline"]
DF["Dockerfile"]
IMG["Container Image"]
REG["Image Registry"]
subgraph OCP["OpenShift Cluster"]
DEP["Deployment"]
POD["Spring Boot Pod"]
SVC["Service"]
ROUTE["Route"]
end
USER["Browser"]
DEV --> GIT
GIT --> BUILD
BUILD --> DF
DF --> IMG
IMG --> REG
REG --> DEP
DEP --> POD
POD --> SVC
SVC --> ROUTE
ROUTE --> USER
Enterprise Banking Example
Payment Service deployment.
flowchart LR
GIT["Git"]
JENKINS["Jenkins"]
BUILD["Docker Build"]
QUAY["Quay Registry"]
OCP["OpenShift"]
POD["Payment Pod"]
CUSTOMERS["Customers"]
GIT --> JENKINS
JENKINS --> BUILD
BUILD --> QUAY
QUAY --> OCP
OCP --> POD
POD --> CUSTOMERS
Benefits:
- Fast deployments
- Rolling updates
- Easy rollback
Common Docker Commands
Build:
docker build -t app .
Run:
docker run -p 8080:8080 app
List images:
docker images
List containers:
docker ps
Remove image:
docker rmi app
Push Image
docker tag springboot-demo quay.io/demo/app:1.0
docker push quay.io/demo/app:1.0
Verify Image
docker inspect springboot-demo
Common Mistakes
❌ Using latest tag
FROM openjdk:latest
Prefer:
FROM eclipse-temurin:21-jre
❌ Running as root
❌ Large image sizes
❌ Installing unnecessary tools
❌ Hardcoding secrets
Never store:
- Passwords
- API Keys
- Database URLs
Use OpenShift Secrets instead.
Best Practices
✅ Multi-stage builds
✅ Small base images
✅ Use non-root users
✅ Enable Actuator
✅ Health probes
✅ Pin image versions
✅ Keep Dockerfile simple
✅ Use .dockerignore
Example:
target/
.git
.idea
README.md
Production Dockerfile
FROM eclipse-temurin:21-jre
WORKDIR /opt/app
COPY target/*.jar app.jar
EXPOSE 8080
ENV JAVA_OPTS="-Xms256m -Xmx512m"
USER 1001
ENTRYPOINT ["sh","-c","java $JAVA_OPTS -jar app.jar"]
Real-World Deployment Workflow
sequenceDiagram
participant Dev as Developer
participant Git
participant CI as Jenkins
participant Docker
participant Registry
participant OCP as OpenShift
participant Pod
participant User as End User
Dev->>Git: Commit & Push
Git->>CI: Webhook Trigger
CI->>Docker: Build Image
Docker->>Registry: Push Image
Registry->>OCP: Image Available
OCP->>Pod: Deploy New Version
Pod-->>User: HTTP Response
Summary
A Dockerfile is the foundation of every containerized Spring Boot application.
Key takeaways:
- Dockerfiles package applications into portable images.
- Multi-stage builds create smaller, cleaner images.
- OpenShift requires secure, non-root containers.
- Layer caching improves build performance.
- Health probes and environment variables make applications production-ready.
- Optimized Dockerfiles reduce deployment time and improve scalability.
Interview Questions
- What is a Dockerfile?
- What does the
FROMinstruction do? - Why use multi-stage builds?
- What is layer caching?
- Why should containers avoid running as root?
- What is the purpose of
ENTRYPOINT? - How do you optimize Docker image size?
- Why use
.dockerignore? - How do OpenShift and Docker work together?
- What are Docker image layers?
Comments
Share a question, correction, or practical insight about this article.
Checking login status...
Loading approved comments...