ECS Architecture Overview
The ADMA deployment uses two ECS services:- Backend Service: Spring Boot API (port 8080)
- Frontend Service: Nginx static site (port 80)
Create ECS Cluster
Create a Fargate-only cluster:FARGATE_SPOT can reduce costs by up to 70% but may interrupt tasks. Use it for non-critical workloads or in combination with regular Fargate.
Create IAM Roles for ECS
ECS tasks require two IAM roles:Task Execution Role
This role allows ECS to pull images from ECR, write logs to CloudWatch, and read secrets from SSM.cat > /tmp/ecs-trust-policy.json <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ecs-tasks.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
aws iam create-role \
--role-name ecsTaskExecutionRole \
--assume-role-policy-document file:///tmp/ecs-trust-policy.json
# Allow ECS to pull from ECR and write to CloudWatch
aws iam attach-role-policy \
--role-name ecsTaskExecutionRole \
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
cat > /tmp/ssm-policy.json <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssm:GetParameters",
"ssm:GetParameter"
],
"Resource": [
"arn:aws:ssm:$AWS_REGION:$ACCOUNT_ID:parameter/adma/prod/*"
]
}
]
}
EOF
aws iam put-role-policy \
--role-name ecsTaskExecutionRole \
--policy-name SSMParameterAccess \
--policy-document file:///tmp/ssm-policy.json
Task Role (Optional)
The task role grants permissions to the application code itself (e.g., to access S3, DynamoDB). For ADMA, this is optional.Create Security Groups
Create security groups for the backend and frontend services:Create CloudWatch Log Groups
Create log groups for container logs:Register Task Definitions
Backend Task Definition
The backend task definition includes environment variables and secrets configuration.ACCOUNT_IDadma-postgres.xxxxx.region.rds.amazonaws.comhttps://go.yourdomain.comhttps://yourdomain.com{
"family": "adma-backend",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"executionRoleArn": "arn:aws:iam::ACCOUNT_ID:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::ACCOUNT_ID:role/ecsTaskRole",
"containerDefinitions": [
{
"name": "backend",
"image": "ACCOUNT_ID.dkr.ecr.eu-west-1.amazonaws.com/adma/backend:latest",
"essential": true,
"portMappings": [
{
"containerPort": 8080,
"hostPort": 8080,
"protocol": "tcp"
}
],
"environment": [
{ "name": "DB_HOST", "value": "adma-postgres.xxxxx.eu-west-1.rds.amazonaws.com" },
{ "name": "DB_PORT", "value": "5432" },
{ "name": "DB_NAME", "value": "urlshortener" },
{ "name": "DB_USERNAME", "value": "appuser" },
{ "name": "APP_BASE_URL", "value": "https://go.yourdomain.com" },
{ "name": "CORS_ALLOWED_ORIGINS", "value": "https://yourdomain.com" },
{ "name": "SERVER_PORT", "value": "8080" },
{ "name": "JWT_EXPIRATION_MS", "value": "86400000" }
],
"secrets": [
{
"name": "DB_PASSWORD",
"valueFrom": "arn:aws:ssm:eu-west-1:ACCOUNT_ID:parameter/adma/prod/DB_PASSWORD"
},
{
"name": "JWT_SECRET",
"valueFrom": "arn:aws:ssm:eu-west-1:ACCOUNT_ID:parameter/adma/prod/JWT_SECRET"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/adma-backend",
"awslogs-region": "eu-west-1",
"awslogs-stream-prefix": "backend"
}
},
"healthCheck": {
"command": [
"CMD-SHELL",
"curl -f http://localhost:8080/actuator/health || exit 1"
],
"interval": 30,
"timeout": 10,
"retries": 3,
"startPeriod": 60
}
}
]
}
Frontend Task Definition
The frontend task definition is simpler since it only serves static files:{
"family": "adma-frontend",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "256",
"memory": "512",
"executionRoleArn": "arn:aws:iam::ACCOUNT_ID:role/ecsTaskExecutionRole",
"containerDefinitions": [
{
"name": "frontend",
"image": "ACCOUNT_ID.dkr.ecr.eu-west-1.amazonaws.com/adma/frontend:latest",
"essential": true,
"portMappings": [
{
"containerPort": 80,
"hostPort": 80,
"protocol": "tcp"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/adma-frontend",
"awslogs-region": "eu-west-1",
"awslogs-stream-prefix": "frontend"
}
},
"healthCheck": {
"command": ["CMD-SHELL", "curl -f http://localhost:80/ || exit 1"],
"interval": 30,
"timeout": 5,
"retries": 3,
"startPeriod": 10
}
}
]
}
Resource Allocation Guide
CPU and Memory Units
Fargate tasks use specific CPU/memory combinations:| vCPU | Memory Options (MB) |
|---|---|
| 0.25 | 512, 1024, 2048 |
| 0.5 | 1024 - 4096 (1 GB increments) |
| 1 | 2048 - 8192 (1 GB increments) |
| 2 | 4096 - 16384 (1 GB increments) |
| 4 | 8192 - 30720 (1 GB increments) |
Recommended Configurations
Environment Variables Reference
Backend Environment Variables
| Variable | Type | Example | Description |
|---|---|---|---|
DB_HOST | Plain | adma-postgres.xxx.rds.amazonaws.com | RDS endpoint |
DB_PORT | Plain | 5432 | PostgreSQL port |
DB_NAME | Plain | urlshortener | Database name |
DB_USERNAME | Plain | appuser | Database user |
DB_PASSWORD | Secret | (SSM) | Database password from SSM |
JWT_SECRET | Secret | (SSM) | JWT signing key from SSM |
JWT_EXPIRATION_MS | Plain | 86400000 | Token expiration (24 hours) |
APP_BASE_URL | Plain | https://go.example.com | Base URL for short links |
CORS_ALLOWED_ORIGINS | Plain | https://example.com | Allowed CORS origins |
SERVER_PORT | Plain | 8080 | Backend port |
Container Health Checks
Health checks determine when a container is ready to receive traffic:Backend Health Check
Spring Boot Actuator provides the
/actuator/health endpoint automatically. It checks database connectivity and application status.Frontend Health Check
Logging Configuration
Logs are sent to CloudWatch Logs using theawslogs driver:
Next Steps
With task definitions registered:- Configure Load Balancer - Set up ALB and routing
- Create ECS services (covered after ALB setup)
- Enable HTTPS/SSL - Secure with certificates
ECS services require the ALB and target groups to exist first. We’ll create the services after setting up the load balancer.