Docs LogoDocs

Microservices & Deployment

Containerizing, CI/CD to GHCR, and deploying behind Nginx.

Microservices & Deployment

Monolith First

Start with a well-layered monolith (like a typical FinTrack-style backend). Split into microservices only when you have a concrete reason: independent scaling needs, separate release cadences, or a team boundary — not because "microservices" sounds more advanced. Splitting too early adds network calls, distributed transactions, and operational overhead for no real benefit.

Core Microservices Building Blocks (Spring Cloud)

ConcernTool
Service discoveryEureka / Consul
API GatewaySpring Cloud Gateway
Config managementSpring Cloud Config Server
ResilienceResilience4j (circuit breaker, retry, bulkhead)
Distributed tracingMicrometer Tracing + Zipkin/Jaeger
Inter-service callsRestClient/WebClient, OpenFeign

Containerizing a Spring Boot App

# Multi-stage build keeps the final image small
FROM eclipse-temurin:21-jdk-alpine AS build
WORKDIR /app
COPY . .
RUN ./mvnw clean package -DskipTests

FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

Use eclipse-temurin (or Spring Boot's own buildpacks via ./mvnw spring-boot:build-image) rather than a full JDK image in production — smaller image, smaller attack surface.

CI/CD: GitHub Actions → GHCR → Oracle Cloud (typical solo-dev pipeline)

# .github/workflows/deploy.yml
name: Build and Deploy
on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          java-version: '21'
          distribution: 'temurin'
      - run: ./mvnw clean package -DskipTests
      - name: Build and push Docker image
        run: |
          docker build -t ghcr.io/${{ github.repository }}:latest .
          echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
          docker push ghcr.io/${{ github.repository }}:latest
      - name: Deploy via SSH
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.ORACLE_HOST }}
          username: ${{ secrets.ORACLE_USER }}
          key: ${{ secrets.ORACLE_SSH_KEY }}
          script: |
            docker pull ghcr.io/${{ github.repository }}:latest
            docker compose up -d

Nginx as a Reverse Proxy in Front of Spring Boot

server {
    listen 443 ssl;
    server_name api.fintrack.example.com;

    location / {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Nginx handles TLS termination, gzip, and can rate-limit/cache at the edge before a request even reaches the JVM.

Health Checks for Orchestration

Whatever deploys your container (Docker Compose, k8s, Oracle Cloud) should poll /actuator/health and restart the container automatically on repeated failures — pairs directly with the Actuator topic.

Last updated on July 15, 2026

On this page