DEV Community

Cover image for Dockerize & Deploy Spring Boot + Angular + MySQL | Full Guide
Smooth Code
Smooth Code

Posted on

Dockerize & Deploy Spring Boot + Angular + MySQL | Full Guide

Containerizing a full-stack application can be challenging—especially when you want to keep builds efficient, secure, and optimized for production. In this guide, we’ll walk through how to dockerize a Spring Boot backend, Angular frontend, and MySQL database using multi-stage Docker builds and Docker Compose.


🧠 Why Multi-Stage Docker Builds?

Traditional Docker builds bundle everything—including build tools and dev dependencies—into the final image. This results in:

❌ Large image sizes
❌ Slower deployments
❌ Unnecessary security exposure

Multi-Stage Builds solve these issues by:

✅ Separating build and runtime environments
✅ Producing smaller, optimized final images
✅ Improving image security

Multi-Stage Builds

🎨 Dockerizing the Angular Frontend

We use a two-stage Dockerfile:

# Build stage
FROM node:lts-slim AS build
WORKDIR /src
COPY package*.json ./
RUN npm ci
COPY . ./
RUN npm run build -- --configuration=production --output-path=dist

# Production stage
FROM nginx:stable AS final
EXPOSE 4200
RUN rm -f /usr/share/nginx/html/index.html
COPY --from=build /src/dist/browser /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
Enter fullscreen mode Exit fullscreen mode

This approach ensures the production image contains only the compiled Angular assets and nginx, without the Node.js build tools.

Angular-Build

nginx.conf (runs frontend on port 4200):

events { }

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    server {
        listen 4200;
        root /usr/share/nginx/html;

        index index.html;
        location / {
            try_files $uri /index.html;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

✅ Final image contains only compiled Angular + Nginx, not Node.js.


🏗️ Dockerizing the Spring Boot Backend

We again use a multi-stage approach:

# Build Stage
FROM eclipse-temurin:17-jdk-jammy AS builder
WORKDIR /app
COPY mvnw .
COPY .mvn/ .mvn/
COPY pom.xml .
COPY src/ src/
RUN ./mvnw clean package -DskipTests

# Runtime Stage
FROM eclipse-temurin:17-jre-jammy AS runtime
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
RUN groupadd -r spring && useradd -r -g spring spring
USER spring
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 9090
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
Enter fullscreen mode Exit fullscreen mode

application.properties

spring.application.name=projectassignment
spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT:3306}/projectassignment?allowPublicKeyRetrieval=true&useSSL=false
spring.datasource.username=${MYSQL_USERNAME:root}
spring.datasource.password=${MYSQL_PASSWORD:root}
spring.jpa.hibernate.ddl-auto=${MYSQL_STRATEGY:update}
server.port=${SERVER_PORT:9090}
Enter fullscreen mode Exit fullscreen mode

🔁 Notice: MYSQL_HOST will be overridden by Docker Compose.


🧱 Docker Compose: Orchestrating All Services

services:
  mysql-db:
    image: mysql:8.0
    container_name: mysql-db
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: projectassignment
    ports:
      - "3306:3306"
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-proot"]
      interval: 10s
      timeout: 5s
      retries: 5

  spring-backend:
    build:
      context: ./project_assignment(backend)
      dockerfile: Dockerfile
    image: pabackend
    container_name: spring-backend
    ports:
      - "9090:9090"
    environment:
      - MYSQL_HOST=mysql-db
    depends_on:
      mysql-db:
        condition: service_healthy
    restart: unless-stopped

  angular-frontend:
    build:
      context: ./project_assignment(frontend)
      dockerfile: Dockerfile
    image: pafrontend
    container_name: angular-frontend
    ports:
      - "4200:4200"
    depends_on:
      - spring-backend
    restart: unless-stopped
Enter fullscreen mode Exit fullscreen mode

🌐 Nginx Reverse Proxy

Nginx Reverse Proxy of angular spring boot app

If you want all traffic via port 80:

server {
    listen 80;
    server_name _;

    location / {
        proxy_pass http://localhost:4200;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_redirect http://localhost:4200/ http://$host/;
    }

    location /api/ {
        proxy_pass http://localhost:9090/;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}
Enter fullscreen mode Exit fullscreen mode

✅ Final Result

After running:

docker compose up --build -d
Enter fullscreen mode Exit fullscreen mode

You will have:

Service URL
Frontend http://localhost:4200
Backend API http://localhost:9090
Database localhost:3306

🎯 Summary

Using multi-stage builds + Docker Compose makes your application:

  • Faster to build
  • Smaller to deploy
  • Easier to maintain
  • More secure in production

Top comments (0)