Deploy SpringBoot JAR and Legacy WAR with Docker + Nginx in 5 Minutes
This guide shows how to containerize Java SpringBoot JAR or traditional WAR projects with Docker and Nginx, providing a unified, reproducible deployment pipeline that eliminates environment conflicts, simplifies port management, and enables seamless development‑to‑production transitions.
Deploying Java applications (SpringBoot JARs or legacy WARs) can be simplified with Docker containers and an Nginx reverse‑proxy. Docker guarantees environment consistency, while Nginx provides a single entry point (ports 80/443), static file serving, HTTPS termination and API proxying.
Prerequisites
Docker and Docker Compose installed on the host.
Application packaged: a runnable .jar for SpringBoot or a .war compatible with Tomcat 8.5/9.0.
Basic knowledge of Docker commands and Nginx reverse‑proxy concepts.
1. Deploying a SpringBoot JAR (recommended)
Project packaging and directory layout
Build the JAR (skip tests for speed): mvn clean package -Dmaven.test.skip=true Resulting JAR is placed in target/. Create the following structure:
docker-java-demo/
├── java/ # Java project files
│ ├── app.jar # Built JAR (e.g., dramamini.jar)
│ └── logs/ # Log directory (mounted at runtime)
├── nginx/ # Nginx configuration
│ ├── nginx.conf
│ └── html/ # Optional static assets
└── docker-compose.yml # Multi‑container definitionCore configuration files
nginx.conf (nginx/nginx.conf) – serves static files and proxies /api/ to the Java container:
worker_processes 1;
events { worker_connections 1024; }
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
# static assets
location / {
root /usr/share/nginx/html;
index index.html;
}
# API reverse proxy
location /api/ {
proxy_pass http://java-app:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}docker-compose.yml – defines Nginx and Java containers on a custom bridge network:
version: '3.8'
services:
nginx:
image: nginx:alpine
container_name: nginx-proxy
ports:
- "80:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/html:/usr/share/nginx/html
- ./nginx/logs:/var/log/nginx
restart: always
networks:
- java-network
depends_on:
- java-app
java-app:
image: openjdk:8-jdk-alpine
container_name: java-app
volumes:
- ./java/app.jar:/app/app.jar
- ./java/logs:/app/logs
working_dir: /app
command: java -jar app.jar --spring.profiles.active=test
restart: always
networks:
- java-network
expose:
- "8080"
networks:
java-network:
driver: bridgeRunning, verifying and updating
Start the stack: docker compose up -d Check container status: docker compose ps Test the deployment:
Static page: http://SERVER_IP API endpoint: http://SERVER_IP/api/xxx View logs if needed:
docker compose logs -f nginx
docker compose logs -f java-appUpdate the JAR – replace ./java/app.jar and restart the container:
docker compose restart java-app2. Deploying a legacy WAR (Tomcat)
Directory layout
docker-war-demo/
├── tomcat/ # Tomcat files
│ ├── webapps/ # Place WAR (e.g., demo.war)
│ └── logs/ # Tomcat logs
├── nginx/ # Re‑use the same Nginx config
│ ├── nginx.conf
│ └── html/
└── docker-compose.yml # Adjusted for Tomcat containerDocker‑Compose changes
Replace the Java service with a Tomcat service that mounts the WAR directory:
services:
nginx:
# same as before
tomcat-app:
image: tomcat:8.5-alpine
container_name: tomcat-app
volumes:
- ./tomcat/webapps:/usr/local/tomcat/webapps
- ./tomcat/logs:/usr/local/tomcat/logs
restart: always
networks:
- java-network
expose:
- "8080"Start and access
cd docker-war-demo/
docker compose up -dTomcat automatically extracts the WAR (e.g., demo.war → demo/). Adjust the Nginx proxy_pass to http://tomcat-app:8080/demo/ and access the API via http://SERVER_IP/api/xxx. Update the WAR by replacing the file under ./tomcat/webapps/ and restarting the container:
docker compose restart tomcat-app3. Production‑grade optimizations
Enable HTTPS
Place a Let’s Encrypt certificate in nginx/cert/ and add a 443 server block:
server {
listen 443 ssl;
server_name your.domain.com;
ssl_certificate /etc/nginx/cert/fullchain.pem;
ssl_certificate_key /etc/nginx/cert/privkey.pem;
# same settings as the port‑80 block
}
server {
listen 80;
server_name your.domain.com;
return 301 https://$host$request_uri;
}Expose the new port in docker-compose.yml:
ports:
- "80:80"
- "443:443"JVM & Tomcat tuning
Java container memory limits: java -Xms1024m -Xmx2048m -jar app.jar Tomcat thread pool (edit server.xml):
maxThreads="200" minSpareThreads="20"High availability (load balancing)
Define an upstream block in Nginx and list multiple Java/Tomcat instances:
http {
upstream java-servers {
server java-app1:8080;
server java-app2:8080;
}
server {
listen 80;
location /api/ {
proxy_pass http://java-servers/;
}
}
}4. Common pitfalls & troubleshooting
Nginx cannot reach the Java container
Verify both containers share the same network: docker network inspect java-network.
Ensure the container name used in proxy_pass matches the actual service name.
Java application fails to start
Check logs: docker compose logs -f java-app.
Confirm the JDK version matches the compiled bytecode and required environment variables are set.
WAR does not auto‑extract
Set proper permissions on the webapps directory: chmod 755 -R ./tomcat/webapps.
Restart the Tomcat container after permission changes.
Conclusion
The Docker + Nginx pattern provides a reproducible, isolated deployment pipeline for both modern SpringBoot JARs and legacy WARs. Docker Compose manages the lifecycle with a single command, while Nginx offers a unified, secure entry point. Optional production tweaks—HTTPS, JVM/Tomcat tuning, and upstream load balancing—allow the solution to scale from a developer sandbox to a robust production environment.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Xiao Liu Lab
An operations lab passionate about server tinkering 🔬 Sharing automation scripts, high-availability architecture, alert optimization, and incident reviews. Using technology to reduce overtime and experience to avoid major pitfalls. Follow me for easier, more reliable operations!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
