Enterprise Jakarta EE applications require extensive tooling during development – Maven for dependency management, full JDKs for compilation, and various build utilities. However, production environments need only the compiled application and runtime server like Payara Server or Payara Micro. Multi-stage Docker builds bridge this gap by separating build and runtime concerns, producing dramatically smaller and more secure container images for Payara deployments.
The Problem with Single-Stage Builds
Traditional single-stage Dockerfiles force you to include everything needed for both building and running your application. A typical Jakarta EE image might contain:
Full JDK (300MB+)
Maven or Gradle (100MB+)
Source code and test files
Intermediate build artifacts
Build caches and temporary files
Jakarta EE runtime like Payara Server
This results in bloated images often exceeding 1GB for simple applications. These oversized containers slow down deployments, consume unnecessary bandwidth, and expand the attack surface by including build tools in production.
Multi-stage Architecture
Multi-stage builds use multiple FROM statements within a single Dockerfile. Each stage serves a specific purpose, and you can selectively copy artifacts between stages while discarding everything else.
For Jakarta EE applications, this typically involves:
Build Stage: Compile source code using Maven with full JDK
Runtime Stage: Deploy the compiled application on a lean Payara image
Implementing a Basic Multi-stage Build
Creating a multi-stage build requires understanding how to structure your Dockerfile for maximum efficiency. The key is separating the build environment from the runtime environment while maintaining clean artifact transfer between stages. Let’s examine a practical example for a standard Jakarta EE web application that demonstrates these principles with Payara Server.
# Build Stage FROM maven:3.9.9-amazoncorretto-21-debian-bookworm AS builder WORKDIR /build COPY pom.xml . RUN mvn dependency:resolve COPY src ./src RUN mvn clean package -DskipTests # Runtime Stage FROM payara/server-full:6.2025.5-jdk21 COPY --from=builder /build/target/myapp.war $DEPLOY_DIR/
This approach noticeably reduce the final image size.
Optimizing Build Performance
One of Docker’s most powerful features is its intelligent layer caching system, but many developers don’t take full advantage of it. When Docker builds an image, it caches each instruction as a separate layer. If nothing has changed in a particular layer, Docker simply reuses the cached version instead of rebuilding it from scratch. This means the order of operations in your Dockerfile can dramatically impact build times, especially during development when you’re rebuilding frequently.
# Build Stage FROM maven:3.9.9-amazoncorretto-21-debian-bookworm AS builder WORKDIR /build # Copy and resolve dependencies first COPY pom.xml . RUN mvn dependency:go-offline # Copy source and build only when code changes COPY src ./src RUN mvn clean package -DskipTests
Notice the mvn dependency:go-offline command? When source code changes, Docker reuses the cached dependency layer, significantly accelerating build times.
Advanced Configuration Management
Real-world production environments rarely run with default configurations. Your applications need database connection pools, custom logging settings, security configurations, and environment-specific properties. The beauty of multi-stage builds is that you can prepare all these configurations during the build phase, ensuring your runtime image contains exactly what it needs without any manual intervention during deployment. For example, you can bundle a custom domain.xml file for your Payara Server with build as shown below.
FROM maven:3.9.9-amazoncorretto-21-debian-bookworm AS builder WORKDIR /build COPY pom.xml . RUN mvn dependency:resolve COPY src ./src COPY config/domain.xml ./config/ RUN mvn package -DskipTests FROM payara/server-full:6.2025.5-jdk21 # Copy application COPY --from=builder /build/target/myapp.war $DEPLOY_DIR/ # Copy custom configuration COPY --from=builder /build/config/domain.xml $PAYARA_DIR/glassfish/domains/domain1/config/ # Set production JVM options ENV JAVA_OPTS="-Xmx2g -Xms1g -XX:+UseG1GC"
Handling External Dependencies
Enterprise Jakarta EE applications often extend beyond basic framework components, requiring additional libraries such as database drivers, messaging clients, or specialized connectors. These dependencies pose a unique challenge in containerization because they must be available at runtime but shouldn’t bloat your build environment. The multi-stage approach allows you to manage these dependencies efficiently by downloading them during the build phase and selectively copying only the required JARs to your runtime image.
FROM maven:3.9.9-amazoncorretto-21-debian-bookworm AS builder WORKDIR /build COPY pom.xml . # Download application and external dependencies RUN mvn dependency:resolve RUN mvn dependency:copy-dependencies -DoutputDirectory=lib COPY src ./src RUN mvn package -DskipTests FROM payara/server-full:6.2025.5-jdk21 COPY --from=builder /build/target/myapp.war $DEPLOY_DIR/ COPY --from=builder /build/lib/*.jar $PAYARA_DIR/glassfish/domains/domain1/lib/
Security Hardening
Multi-stage builds provide inherent security benefits by eliminating build tools and development dependencies from production images, but you can further enhance security through deliberate configuration choices. Running containers as non-privileged users, implementing health checks, and maintaining proper file ownership are essential practices that reduce attack vectors and improve overall container security posture.
Microservices architectures benefit significantly from Payara Micro’s lightweight footprint and rapid startup times. Unlike the full Payara Server, Payara Micro is designed specifically for cloud-native deployments and containerized environments. When implementing multi-stage builds with Payara Micro, you can achieve even smaller final images while maintaining the full Jakarta EE capabilities your applications require.
FROM maven:3.9.9-amazoncorretto-21-debian-bookworm AS builder WORKDIR /build COPY pom.xml . RUN mvn dependency:resolve COPY src ./src RUN mvn package -DskipTests FROM payara/micro:6.2025.5-jdk21 COPY --from=builder /build/target/myapp.war $DEPLOY_DIR/ CMD ["java", "-jar", "payara-micro.jar", "--deploy", "/opt/payara/deployments/myapp.war", "--port", "8080"]
Payara Micro images are significantly smaller than full server images, making them ideal for containerized microservices.
Build Automation and CI/CD Integration
Modern development workflows require integrations between your containerization strategy and continuous integration pipelines. Multi-stage builds work particularly well with automated build systems because they provide consistent, reproducible results while maintaining clear separation between development and production concerns. Implementing proper build automation ensures your team can deploy efficiently while maintaining quality standards.
#!/bin/bash APP_NAME="myapp" VERSION=${1:-latest} REGISTRY="your-registry.com" echo "Building ${APP_NAME}:${VERSION}" docker build -t ${REGISTRY}/${APP_NAME}:${VERSION} . echo "Pushing to registry" docker push ${REGISTRY}/${APP_NAME}:${VERSION} # Tag as latest if building main branch if [[ "${CI_COMMIT_REF_NAME}" == "main" ]]; then docker tag ${REGISTRY}/${APP_NAME}:${VERSION} ${REGISTRY}/${APP_NAME}:latest docker push ${REGISTRY}/${APP_NAME}:latest
Most Jakarta EE applications see 60-80% size reductions and 40-60% faster deployment times with properly configured multi-stage builds.
Best Practices Summary
Structure your Dockerfile with stable dependencies first, followed by frequently changing source code. Use specific version tags for base images to ensure reproducible builds across environments. Keep build artifacts minimal – copy only what’s necessary for runtime. Include health checks and proper logging configuration for production readiness.
Consider using .dockerignore files to exclude unnecessary files from build contexts:
.git/ target/ *.md .gitignore Dockerfile
Common Pitfalls to Avoid
Don’t copy entire directories indiscriminately – be explicit about what moves between stages. Avoid installing unnecessary packages in runtime stages. Remember that environment variables set in build stages don’t automatically carry over to runtime stages.
Conclusion
Multi-stage Docker builds represent a paradigm shift toward more efficient container strategies. Through a separation of build and runtime concerns, you create smaller, more secure, and faster-deploying Jakarta EE applications with Payara Server. The investment in restructuring your build process pays immediate dividends in deployment efficiency and long-term maintainability.
The combination of Jakarta EE’s enterprise capabilities with Payara’s performance and Docker’s containerization efficiency creates a solid foundation for modern application deployments. Ready to get started? Download Payara Server Community for your development environment and explore multi-stage builds with your own Jakarta EE applications. When you’re ready to deploy to production, Payara Enterprise provides the enterprise-grade support, security patches, and performance optimizations your mission-critical applications demand.
Beyond Docker: A More Stable Path for Production Jakarta EE and MicroProfile Apps
Explore why moving beyond Docker can future-proof your Jakarta EE and MicroProfile applications. This free guide reveals how Payara Cloud offers a more stable, scalable, and production-ready path — eliminating the pain of managing containers while accelerating your deployment workflow.
How to Run and Scale AI Java Applications in Production: An Overview for Developers with no Machine Learning Expertise
Organizations are increasingly interested in adopting artificial intelligence (AI) and generative AI (GenAI) to improve operations and offer next-generation […]
6 minutes
Community
Gaurav Gupta
12 Sep 2025
Boost Developer Productivity with Payara Server Maven Plugin + AI Agent
Managing Payara Server Just Got Smarter Imagine managing your Jakarta EE applications not just with Maven goals, but by […]
3 minutes
Jakarta EE
Asif Khan
10 Sep 2025
Securing Jakarta EE Application Servers Needs Executive Attention
If your organization runs Jakarta EE applications, securing the application server they rely on is not a one-time project. […]