Skip to main content

Overview

Portfolio Hub API uses a multi-stage Docker build for optimal performance and security. The application is containerized with MySQL database support via Docker Compose.
The multi-stage build reduces the final image size by excluding build tools and only including the runtime environment.

Prerequisites

Before deploying with Docker, ensure you have:
1

Install Docker

Download and install Docker Desktop for your operating system.
2

Install Docker Compose

Docker Compose is included with Docker Desktop. For Linux, install it separately:
sudo apt-get update
sudo apt-get install docker-compose-plugin
3

Verify Installation

Check that Docker and Docker Compose are properly installed:
docker --version
docker compose version

Dockerfile Architecture

The Portfolio Hub API uses a multi-stage Docker build with two stages:

Stage 1: Build Stage

This stage compiles the Spring Boot application using Maven:
# --- Etapa 1: BUILD ---
FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /app

# 1. Optimización de Capa de Caché de Docker:
COPY pom.xml .

# 2. Descargamos todas las dependencias.
RUN mvn dependency:go-offline

# 3. Copiamos el código fuente de la aplicación.
COPY src ./src

# 4. Compilamos la aplicación y creamos el .jar.
RUN mvn package -DskipTests
By copying pom.xml first and running mvn dependency:go-offline, Docker caches the dependencies layer. This means subsequent builds only re-download dependencies if pom.xml changes, significantly speeding up build times.

Stage 2: Production Stage

This stage creates a minimal runtime image:
# --- Etapa 2: PRODUCTION ---
FROM eclipse-temurin:21-jre-jammy AS production
WORKDIR /app

# Definimos el puerto de la aplicación como un argumento.
ARG APP_PORT=8080
ENV PORT=${APP_PORT}

# Expone el puerto 8080 del contenedor a la red interna de Docker.
EXPOSE ${APP_PORT}

# 1. Mejor Práctica de Seguridad: Crear un usuario no privilegiado.
RUN addgroup --system spring && adduser --system --ingroup spring springuser
USER springuser

# 2. Copiamos el artefacto.jar compilado desde la etapa 'build'.
COPY --from=build /app/target/*.jar app.jar

# 3. ENTRYPOINT (Forma Exec):
ENTRYPOINT ["java", "-jar", "app.jar"]
The production stage uses a JRE-only image (not JDK) and runs as a non-root user for enhanced security.

Building the Docker Image

Build Manually

To build the Docker image manually:
docker build -t portfolio-hub-api:latest .
docker build --build-arg APP_PORT=9090 -t portfolio-hub-api:latest .

Verify the Image

Check that the image was created successfully:
docker images | grep portfolio-hub-api

Running with Docker Compose

The recommended way to run Portfolio Hub API is using Docker Compose, which orchestrates both the application and MySQL database.

Docker Compose Configuration

version: '3.8'
services:
  portfolio-api:
    build: .
    container_name: portfolio-api-container
    ports:
      - '8080:8080'
    env_file:
      - .env
    restart: unless-stopped
    depends_on:
      - mysql-db
  mysql-db:
    image: 'mysql:8.0'
    container_name: mysql-db-container
    restart: unless-stopped
    ports:
      - '3307:3306'
    environment:
      MYSQL_ROOT_PASSWORD: '${MYSQL_ROOT_PASSWORD}'
      MYSQL_DATABASE: '${MYSQL_DATABASE}'
      MYSQL_USER: '${MYSQL_USER}'
      MYSQL_PASSWORD: '${MYSQL_PASSWORD}'
    volumes:
      - 'mysql-data:/var/lib/mysql'
volumes:
  mysql-data: null
The MySQL container exposes port 3307 on the host to avoid conflicts with existing MySQL installations using the default port 3306.

Environment File Setup

Create a .env file in the project root with all required environment variables:
.env
# MySQL Database
MYSQL_HOST=mysql-db
MYSQL_PORT=3306
MYSQL_DATABASE=studiostkoh.portafolio
MYSQL_USER=portfolio_user
MYSQL_PASSWORD=secure_password_here
MYSQL_ROOT_PASSWORD=root_password_here

# JWT Configuration
JWT_TOKEN=your_very_long_secret_key_for_jwt_tokens_min_256_bits
JWT_EXPIRATION_TIME=60

# Google Drive OAuth
DRIVE_OAUTH_CLIENT_ID=your_client_id
DRIVE_OAUTH_CLIENT_SECRET=your_client_secret
DRIVE_OAUTH_REFRESH_TOKEN=your_refresh_token

# Google Drive Folders
DRIVE_FOLDER_USER_AVATARS=folder_id_for_avatars
DRIVE_FOLDER_USER_RESUMES=folder_id_for_resumes
DRIVE_FOLDER_PROJECTS_COVER=folder_id_for_covers
DRIVE_FOLDER_SKILLS_ICON=folder_id_for_icons
DRIVE_FOLDER_CERTIFICATES=folder_id_for_certificates

# SMTP Email
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
GMAIL_APP_EMAIL=[email protected]
GMAIL_APP_PASSWORD=your_app_password

# CORS
CORS_ALLOWED_ORIGINS=http://localhost:3000,https://yourdomain.com
Never commit the .env file to version control. Add it to .gitignore to protect sensitive credentials.

Start the Services

1

Start in Detached Mode

Launch both the API and database containers:
docker compose up -d
2

View Logs

Monitor the application startup:
docker compose logs -f portfolio-api
3

Verify Services

Check that both containers are running:
docker compose ps

Access the Application

Once started, access:
  • API: http://localhost:8080
  • Swagger UI: http://localhost:8080/swagger-ui/index.html
  • MySQL: localhost:3307 (from host machine)

Docker Compose Commands

docker compose up -d
Using docker compose down -v will delete all database data. Only use this for development or when you want a clean slate.

Running Container Manually

If you prefer to run the container without Docker Compose:

1. Create Docker Network

docker network create portfolio-network

2. Run MySQL Container

docker run -d \
  --name mysql-db-container \
  --network portfolio-network \
  -e MYSQL_ROOT_PASSWORD=root_password \
  -e MYSQL_DATABASE=studiostkoh.portafolio \
  -e MYSQL_USER=portfolio_user \
  -e MYSQL_PASSWORD=secure_password \
  -p 3307:3306 \
  -v mysql-data:/var/lib/mysql \
  mysql:8.0

3. Run API Container

docker run -d \
  --name portfolio-api-container \
  --network portfolio-network \
  -p 8080:8080 \
  -e MYSQL_HOST=mysql-db-container \
  -e MYSQL_PORT=3306 \
  -e MYSQL_DATABASE=studiostkoh.portafolio \
  -e MYSQL_USER=portfolio_user \
  -e MYSQL_PASSWORD=secure_password \
  -e JWT_TOKEN=your_secret_key \
  -e JWT_EXPIRATION_TIME=60 \
  --env-file .env \
  portfolio-hub-api:latest

Troubleshooting

Check the container logs:
docker compose logs portfolio-api
Common issues:
  • Missing environment variables
  • Database connection errors
  • Port conflicts
Ensure the MySQL container is running:
docker compose ps mysql-db
Verify the MYSQL_HOST is set to mysql-db (the service name) in your .env file.
If port 8080 or 3307 is already in use, modify the ports in docker-compose.yml:
ports:
  - '9090:8080'  # Map to port 9090 instead
The application uses Flyway for database migrations. If you see migration errors:
  1. Check that the database schema exists
  2. Verify Flyway baseline settings
  3. Clear the database and restart:
docker compose down -v
docker compose up -d

Production Considerations

Use External Database

For production, use a managed database service (AWS RDS, Google Cloud SQL) instead of a containerized MySQL instance.

Enable Health Checks

Add health check endpoints and configure Docker health checks in your compose file.

Use Secrets Management

Use Docker Secrets or external secret managers (AWS Secrets Manager, HashiCorp Vault) instead of .env files.

Enable Monitoring

Integrate monitoring tools like Prometheus and Grafana to track container metrics.

Next Steps

Environment Variables

Learn about all configuration options

API Reference

Explore available endpoints

Build docs developers (and LLMs) love