How to Deploy Java Spring Boot Microservices in AWS?

 

0) Pick your runtime (decision in 1 minute)

Need Best fit
Lowest ops, containers ECS on Fargate (no servers)
Kubernetes ecosystem EKS (managed control plane)
Fastest “just ship it” (single service) Elastic Beanstalk
Event‑driven, tiny functions Lambda (Spring Cloud Function)
Full control / legacy lift‑and‑shift EC2 Auto Scaling

Recommendation for most teams: Start with ECS Fargate + ALB + RDS/Aurora, then evolve to EKS or service mesh only when you truly need it.


1) Prepare your services

Spring Boot: 3.x, JDK 21, health endpoints (/actuator/health, /actuator/metrics).

Packaging: Containerize each service.

# Dockerfile (multi-stage)
FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /app
COPY pom.xml ./
COPY src ./src
RUN mvn -q -DskipTests package

FROM eclipse-temurin:21-jre
ENV JAVA_OPTS="-XX:+UseG1GC -XX:MaxRAMPercentage=75"
WORKDIR /app
COPY --from=build /app/target/app.jar /app/app.jar
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=2s --retries=3 CMD curl -f http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["sh","-c","java $JAVA_OPTS -jar /app/app.jar"]

Micrometer for metrics; OpenTelemetry for traces; Flyway/Liquibase for DB migrations.


2) Core AWS building blocks

  • VPC: 2–3 AZs, private subnets for services, public subnets for ALBs; NAT in each AZ.
  • ECR: private image registry.
  • Compute: ECS Fargate (or EKS).
  • Networking: ALB (HTTP/HTTPS), API Gateway optional for public APIs.
  • Service discovery: AWS Cloud Map (ECS/EKS) or K8s DNS if on EKS.
  • Data: RDS/Aurora (PostgreSQL/MySQL), ElastiCache (Redis), S3.
  • Config & secrets: AWS SSM Parameter Store / Secrets Manager.
  • Messaging: SQS (queues), SNS (pub/sub), EventBridge (events/schedules).
  • Observability: CloudWatch logs/metrics, X‑Ray (or OTel → Prometheus/Grafana), alarms (CW Alarms).

3) CI/CD (container flow)

  1. CI: Build, test, scan, push image → ECR.
  2. CD: Update ECS service (or EKS Deployment). Use blue/green (CodeDeploy) or rolling.

GitHub Actions example (CI only):

name: build-and-push
on: [push]
jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with: { distribution: temurin, java-version: '21' }
      - run: mvn -q -DskipTests package
      - run: echo ${{ secrets.AWS_ACCESS_KEY_ID }} | sed 's/./&/g' > /dev/null  # placeholder
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-south-1
      - uses: aws-actions/amazon-ecr-login@v2
      - run: |
          IMAGE=xxxxxxxxxx.dkr.ecr.ap-south-1.amazonaws.com/orders
          docker build -t $IMAGE:${{ github.sha }} .
          docker push $IMAGE:${{ github.sha }}

4) ECS on Fargate (reference setup)

Task definition (essentials):

{
  "family": "orders",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "512",
  "memory": "1024",
  "executionRoleArn": "arn:aws:iam::123:role/ecsTaskExecutionRole",
  "taskRoleArn": "arn:aws:iam::123:role/ordersTaskRole",
  "containerDefinitions": [{
    "name": "orders",
    "image": "123.dkr.ecr.ap-south-1.amazonaws.com/orders:{{TAG}}",
    "portMappings": [{ "containerPort": 8080, "protocol": "tcp" }],
    "environment": [
      { "name":"SPRING_PROFILES_ACTIVE","value":"prod" }
    ],
    "secrets": [
      {"name":"DB_URL","valueFrom":"arn:aws:secretsmanager:ap-south-1:123:secret:ordersDbUrl"},
      {"name":"JWT_SECRET","valueFrom":"arn:aws:ssm:ap-south-1:123:parameter/jwt/secret"}
    ],
    "logConfiguration": {
      "logDriver": "awslogs",
      "options": {
        "awslogs-group": "/ecs/orders",
        "awslogs-region": "ap-south-1",
        "awslogs-stream-prefix": "ecs"
      }
    },
    "healthCheck": {
      "command": ["CMD-SHELL","curl -f http://localhost:8080/actuator/health || exit 1"],
      "interval": 30, "timeout": 5, "retries": 3, "startPeriod": 10
    }
  }]
}

Service + ALB notes:

  • Create an ECS Service (Fargate), attach to Target Group (HTTP 8080 health check → /actuator/health), fronted by an ALB with HTTPS (ACM cert).
  • Auto scaling: scale on CPU/RPS/RequestCountPerTarget.
  • Service discovery: enable Cloud Map (orders.svc.local) if you need service‑to‑service lookups.

5) EKS (if you choose Kubernetes)

  • Use AWS Load Balancer Controller for ALB/Ingress.
  • K8s manifests (per service):
apiVersion: apps/v1
kind: Deployment
metadata: { name: orders }
spec:
  replicas: 3
  selector: { matchLabels: { app: orders } }
  template:
    metadata: { labels: { app: orders } }
    spec:
      containers:
      - name: orders
        image: 123.dkr.ecr.ap-south-1.amazonaws.com/orders:1.0.0
        ports: [{ containerPort: 8080 }]
        env:
          - name: SPRING_PROFILES_ACTIVE
            value: prod
          - name: DB_URL
            valueFrom:
              secretKeyRef: { name: orders-secrets, key: DB_URL }
        readinessProbe: { httpGet: { path: /actuator/health, port: 8080 }, initialDelaySeconds: 5 }
        livenessProbe:  { httpGet: { path: /actuator/health, port: 8080 }, initialDelaySeconds: 15 }
---
apiVersion: v1
kind: Service
metadata: { name: orders }
spec:
  selector: { app: orders }
  ports: [{ port: 80, targetPort: 8080 }]
  • Add ConfigMaps/Secrets from SSM/Secrets Manager using external secrets operator if desired.
  • Optional service mesh: App Mesh/Istio for mTLS, retries, canaries.

6) Data, config, and secrets

  • RDS/Aurora: put in private subnets; SGs only from service SGs.
  • Migrations via Flyway on startup or Job/one‑off Task before rollout.
  • Parameter Store / Secrets Manager for DB creds, tokens; IAM roles for tasks/pods (IRSA on EKS) → no static keys.

Spring Boot config example (application-prod.yml):

server:
  port: 8080
management.endpoints.web.exposure.include: health,metrics,prometheus
spring:
  datasource:
    url: ${DB_URL}
  jackson.serialization.WRITE_DATES_AS_TIMESTAMPS: false

7) Traffic management & security

  • HTTPS everywhere: ACM cert on ALB/API Gateway.
  • Auth: Spring Security + JWT / OIDC (Cognito) at the edge or per service.
  • Network: Security Groups restrict east‑west traffic; VPC endpoints (S3, SSM, Secrets) to avoid public egress.
  • WAF on ALB/API Gateway for public APIs.
  • Rate limiting at the gateway; SQS buffering for spikes on write paths.

8) Observability & SLOs

  • Logs: JSON logs to CloudWatch; include traceId/spanId.
  • Metrics: Micrometer → CloudWatch or Prometheus; RED/Golden signals dashboards.
  • Tracing: OpenTelemetry SDK + Collector → X‑Ray/Jaeger/Tempo.
  • Alarms: 5xx rate, p95 latency, CPU/memory, queue depth; on-call routing via SNS.

Spring Boot (Micrometer)

<dependency>
  <groupId>io.micrometer</groupId><artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

9) Safe deployments

  • Rolling (default) for minor changes.
  • Blue/Green with CodeDeploy (ECS) or Canary (EKS + Argo Rollouts): start at 5–10%, watch SLOs, then promote.
  • Feature flags (Unleash/LaunchDarkly) for risky features independent of deploys.
  • Rollback: keep N previous task definitions / deployments; DB expand‑contract migrations.

10) Reference microservices layout (AWS‑friendly)

/orders
  Dockerfile
  pom.xml
  src/...
/payments
  Dockerfile
  ...
/infra (Terraform)
  vpc.tf
  ecs_cluster.tf
  rds.tf
  alb.tf
  ecr.tf
/cicd
  github-actions.yml
  codepipeline/ (optional)

Quick checklist (print this)

  • One repo per service; immutable, signed images in ECR
  • ECS Fargate (or EKS) across ≥2 AZs; ALB with HTTPS/ACM
  • Secrets Manager/SSM for secrets/config; IAM roles for tasks/pods
  • RDS/Aurora in private subnets; migrations with Flyway/Liquibase
  • Autoscaling + health checks; blue/green or canary with auto‑rollback
  • Micrometer + OTel wired; CloudWatch alarms on SLOs
  • SQS/EventBridge for async flows; API Gateway/ALB at the edge
  • WAF, SGs, VPC endpoints; no hard‑coded creds
Back to blog

Leave a comment

Please note, comments need to be approved before they are published.