What you'll learn
Code isn't done until it runs where users can reach it. This module covers the last mile: packaging your application, configuring it for different environments, containerizing with Docker, and deploying and monitoring it in production.
By the end you'll be able to:
- Package an app as a runnable (fat) JAR
- Build slim runtimes and native installers with
jlinkandjpackage - Configure apps per environment without rebuilding
- Containerize with Docker
- Understand deployment and production monitoring
JARs & fat JARs
You met JARs in Module 11 — a bundle of your compiled classes. But your app also depends on libraries. A fat JAR (or "uber JAR") packs your code and all its dependencies into a single executable file, so deployment is just "copy one JAR and run it." Build tools produce these for you:
# A build tool bundles your code + all dependencies into one
# runnable "fat" (uber) JAR — nothing else to install but a JRE.
mvn package
java -jar target/app.jar # runs anywhere Java is installedjlink & jpackage
For more control, the JDK offers two tools. jlink builds a custom, minimal Java runtime containing only the modules your app uses — smaller images, faster startup. jpackage goes further and creates a native installer (.exe, .dmg, .deb) that bundles a runtime, so users can install your app without having Java at all.
Configuration & environments
The same application must run in different environments — your laptop, a test server, production — each with different database URLs, ports, and keys. The rule: build once, configure per environment. Read settings from environment variables or external config files, never hardcode them:
// Read configuration from the environment, not hardcoded values —
// the SAME build then runs in dev, staging, and production.
String dbUrl = System.getenv("DATABASE_URL");
int port = Integer.parseInt(
System.getProperty("server.port", "8080")); // 8080 is the defaultNote
Docker
Docker packages your app and its entire environment — the JRE, OS libraries, config — into a portable container image that runs identically everywhere. This finally kills "works on my machine." A short Dockerfile describes how to build the image:
# Start from an image that already has Java 21
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY target/app.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]Tip
Deploying & monitoring
With a JAR or container in hand, deployment means running it on a server or cloud platform (AWS, Google Cloud, Azure) — increasingly automated by the CI/CD pipeline from Module 32. But shipping isn't the end:
In production you need observability: centralised logs (Module 30), metrics (request rates, latency, error rates, memory), and alerts that page you when something breaks. Java tools like Micrometer and Java Flight Recorder, paired with dashboards (Grafana), keep you informed about your live system.
Recap & quick check
Key takeaways
- A fat/uber JAR bundles your code plus all dependencies into one runnable file.
- jlink builds a minimal custom runtime; jpackage builds native installers.
- Build once, configure per environment — read settings from env vars/config, never hardcode.
- Docker packages the app plus its environment into a portable container image.
- Production needs observability: centralised logs, metrics, and alerts.
Quick check
1. What is a fat (uber) JAR?
2. How should environment-specific settings (DB URL, port) be provided?
3. What does Docker package into a container image?
4. What does jpackage produce?
5. Why is monitoring important after deployment?
Excellent — you've completed Phase 5 and can take software all the way to production. One phase remains: Phase 6 — the Capstone Projects that turn knowledge into a portfolio.