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)
- CI: Build, test, scan, push image → ECR.
- 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