Deploy Spring Boot applications with Jetty and Docker containers

Intermediate 45 min Apr 11, 2026 797 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Learn to deploy production-ready Spring Boot applications using Jetty embedded server with Docker containerization. This tutorial covers multi-stage Docker builds, production configuration, and deployment with Docker Compose for scalable Java applications.

Prerequisites

  • Root or sudo access
  • At least 4GB RAM
  • Docker and Java 21 installed

What this solves

Spring Boot applications with embedded Jetty server provide a lightweight alternative to traditional Tomcat deployments. Docker containerization enables consistent deployments across environments while multi-stage builds optimize image sizes for production use.

Step-by-step installation

Install Docker and Java development tools

Start by installing Docker Engine and Java JDK for building Spring Boot applications.

sudo apt update && sudo apt upgrade -y
sudo apt install -y docker.io docker-compose openjdk-21-jdk maven
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
sudo dnf update -y
sudo dnf install -y docker docker-compose java-21-openjdk-devel maven
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
Note: Log out and back in after adding your user to the docker group for permissions to take effect.

Verify Java and Docker installation

Confirm all required tools are properly installed and accessible.

java --version
mvn --version
docker --version
docker compose version

Create Spring Boot project structure

Generate a new Spring Boot project with Jetty dependencies using Maven.

mkdir springboot-jetty-app
cd springboot-jetty-app
mvn archetype:generate -DgroupId=com.example -DartifactId=jetty-app -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

Configure Maven dependencies for Spring Boot with Jetty

Replace the default pom.xml with Spring Boot starter dependencies and exclude Tomcat in favor of Jetty.


Create Spring Boot application class

Create the main application class with a sample REST endpoint for testing.

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class Application {
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    
    @GetMapping("/")
    public String home() {
        return "Spring Boot with Jetty Server - Running in Docker Container";
    }
    
    @GetMapping("/health")
    public String health() {
        return "OK - Jetty Server is running";
    }
}

Configure application properties

Set Jetty server configuration and production settings in application properties.

# Server configuration
server.port=8080
server.jetty.threads.max=200
server.jetty.threads.min=10
server.jetty.threads.idle-timeout=60000
server.jetty.connection-idle-timeout=30000

# Actuator endpoints
management.endpoints.web.exposure.include=health,info,metrics
management.endpoint.health.show-details=always
management.server.port=8081

# Logging configuration
logging.level.org.eclipse.jetty=INFO
logging.level.com.example=DEBUG
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} - %msg%n

Create multi-stage Dockerfile

Build an optimized Docker image using multi-stage builds to minimize the final image size.

# Build stage
FROM openjdk:21-jdk-slim AS build

WORKDIR /app

# Copy pom.xml and download dependencies
COPY pom.xml .
RUN apt-get update && apt-get install -y maven
RUN mvn dependency:go-offline -B

# Copy source code and build application
COPY src ./src
RUN mvn clean package -DskipTests

# Runtime stage
FROM openjdk:21-jre-slim AS runtime

# Create non-root user for security
RUN groupadd -r appgroup && useradd -r -g appgroup appuser

# Install curl for health checks
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Copy JAR from build stage
COPY --from=build /app/target/jetty-app-1.0.0.jar app.jar

# Change ownership to non-root user
RUN chown -R appuser:appgroup /app
USER appuser

# Expose ports
EXPOSE 8080 8081

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
    CMD curl -f http://localhost:8081/actuator/health || exit 1

# Run application
ENTRYPOINT ["java", "-jar", "-Djava.security.egd=file:/dev/./urandom", "app.jar"]

Create Docker Compose configuration

Set up Docker Compose for easy deployment with environment-specific configurations.

version: '3.8'

services:
  springboot-jetty:
    build:
      context: .
      dockerfile: Dockerfile
    image: springboot-jetty:latest
    container_name: springboot-jetty-app
    ports:
      - "8080:8080"
      - "8081:8081"
    environment:
      - SPRING_PROFILES_ACTIVE=docker
      - JAVA_OPTS=-Xmx512m -Xms256m
    restart: unless-stopped
    networks:
      - app-network
    volumes:
      - ./logs:/app/logs
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8081/actuator/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

  nginx:
    image: nginx:alpine
    container_name: nginx-proxy
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - springboot-jetty
    restart: unless-stopped
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

volumes:
  logs:
    driver: local

Configure Nginx reverse proxy

Create Nginx configuration to proxy requests to the Spring Boot application with load balancing capabilities.

events {
    worker_connections 1024;
}

http {
    upstream springboot {
        server springboot-jetty:8080;
    }
    
    server {
        listen 80;
        server_name localhost;
        
        # Security headers
        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;
        add_header X-XSS-Protection "1; mode=block";
        
        location / {
            proxy_pass http://springboot;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            
            # Timeouts
            proxy_connect_timeout 30s;
            proxy_send_timeout 30s;
            proxy_read_timeout 30s;
        }
        
        location /actuator/health {
            proxy_pass http://springboot-jetty:8081/actuator/health;
            access_log off;
        }
    }
}

Create production environment configuration

Add production-specific application properties for Docker deployment.

# Production configuration for Docker
server.jetty.threads.max=300
server.jetty.threads.min=20

# Logging configuration for production
logging.level.com.example=INFO
logging.file.name=/app/logs/application.log
logging.logback.rollingpolicy.max-file-size=100MB
logging.logback.rollingpolicy.max-history=30

# Actuator security
management.endpoints.web.base-path=/actuator
management.endpoint.health.show-components=always

Build and deploy the application

Build the Docker images and start the complete application stack.

cd jetty-app

# Build the application
docker compose build

# Start the services
docker compose up -d

# View logs
docker compose logs -f springboot-jetty

Verify your setup

Test the Spring Boot application and verify all components are working correctly.

# Check container status
docker compose ps

# Test application endpoint
curl http://localhost/

# Check health endpoint
curl http://localhost/actuator/health

# View application metrics
curl http://localhost:8081/actuator/metrics

# Check Jetty server info in logs
docker compose logs springboot-jetty | grep -i jetty

You should see responses indicating the Spring Boot application is running with Jetty server. The health check should return status "UP" and metrics should show Jetty-specific measurements.

Production optimization

Configure JVM memory settings

Optimize JVM memory allocation for containerized environments.

version: '3.8'

services:
  springboot-jetty:
    environment:
      - JAVA_OPTS=-Xmx1g -Xms512m -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0
      - JVM_ARGS=-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap

Add container resource limits

Set resource constraints to prevent container resource exhaustion.

services:
  springboot-jetty:
    deploy:
      resources:
        limits:
          cpus: '1.5'
          memory: 1G
        reservations:
          cpus: '0.5'
          memory: 512M

Common issues

SymptomCauseFix
Port binding failedPort already in usesudo netstat -tlnp | grep :8080 to find conflicting process
Application won't startInsufficient memoryIncrease Docker memory limits or reduce -Xmx value
Container exits immediatelyJava application errordocker compose logs springboot-jetty to check startup errors
Health check failingManagement port not accessibleVerify port 8081 is exposed and actuator endpoints enabled
Nginx 502 Bad GatewayBackend connection failedCheck if springboot-jetty container is healthy and network connectivity
Build fails with Maven errorsDependencies not downloadedRun docker compose build --no-cache to rebuild images

Next steps

Automated install script

Run this to automate the entire setup

Don't want to manage this yourself?

We handle infrastructure for businesses that depend on uptime. Fully managed, with one fixed contact who knows your setup.

You get one fixed contact who knows your setup

Rotterdam 01:25 · reachable in a message, no ticket form