This section covers Steps 13–15: building and pushing Docker images to ECR, deploying the OCR service on ECS Fargate, and deploying the Backend on Elastic Beanstalk.
aws ecr create-repository --repository-name smartinvoice-backend --region ap-southeast-1
aws ecr create-repository --repository-name smartinvoice-ocr --region ap-southeast-1
(Or manually via Console: ECR → Repositories → Create repository)
Replace <ACCOUNT_ID> with your 12-digit AWS Account ID:
aws ecr get-login-password --region ap-southeast-1 | \
docker login --username AWS --password-stdin <ACCOUNT_ID>.dkr.ecr.ap-southeast-1.amazonaws.com
# Move into the API folder
cd SmartInvoice.API
# Build Docker image
docker build -t smartinvoice-backend .
# Tag to match the ECR repository
docker tag smartinvoice-backend:latest <ACCOUNT_ID>.dkr.ecr.ap-southeast-1.amazonaws.com/smartinvoice-backend:latest
# Push to AWS
docker push <ACCOUNT_ID>.dkr.ecr.ap-southeast-1.amazonaws.com/smartinvoice-backend:latest
[!NOTE] The OCR image is large (~2–3 GB). Ensure a stable internet connection.
# Move into the OCR folder
cd ../invoice_ocr
# Build Docker image
docker build -t smartinvoice-ocr .
# Tag
docker tag smartinvoice-ocr:latest <ACCOUNT_ID>.dkr.ecr.ap-southeast-1.amazonaws.com/smartinvoice-ocr:latest
# Push to AWS
docker push <ACCOUNT_ID>.dkr.ecr.ap-southeast-1.amazonaws.com/smartinvoice-ocr:latest
[!TIP] If you get a “Permission Denied” error on push, verify that your IAM User has the
AmazonEC2ContainerRegistryFullAccesspolicy.
Console: ECS → Clusters → Create
| Field | Value |
|---|---|
| Cluster name | smartinvoice-cluster |
| Infrastructure | Fargate only |
Console: ECS → Task definitions → Create new task definition

| Field | Value | |
|---|---|---|
| Family | smartinvoice-ocr-task | |
| Launch type | AWS Fargate | |
| OS/Architecture | Linux/X86_64 | |
| CPU | 2 vCPU | |
| Memory | 4 GB | |
| Task role | smartinvoice-ecs-task-role | |
| Execution role | ecsTaskExecutionRole | |
| Container name | ocr-container | |
| Image URI | ECR URI from step 13.4 | |
| Port | 5000 | |
| Environment | DEVICE=cpu, HOST=0.0.0.0, PORT=5000 | |
| Logs | awslogs → /ecs/smartinvoice-ocr-task | \ |

[!TIP] Cost Saving: Cloud Map allows services to discover each other via internal DNS (e.g.,
ocr.smartinvoice.local), saving approximately $18/month by eliminating the need for an Internal Load Balancer.
Console: AWS Cloud Map → Create namespace
| Field | Value |
|---|---|
| Namespace name | smartinvoice.local |
| Instance discovery | API calls and DNS queries in VPCs (Private) |
| VPC | smartinvoice-vpc |

When creating the ECS Service in the next step, AWS will automatically register the IP addresses of your running tasks into the ocr.smartinvoice.local domain name. This allows the Backend to call the OCR service directly.
Console: ECS → Clusters → smartinvoice-cluster → Services → Create

smartinvoice-ocr-task (LATEST)smartinvoice-ocr-task-service2
smartinvoice-vpcsmartinvoice-ocr-sg
smartinvoice.local.ocr.A record.15 / 60 seconds.
→ Click Create ✅. Once the Service is Running, update the /SmartInvoice/prod/OCR_API_ENDPOINT parameter in SSM to http://ocr.smartinvoice.local:5000.
| Field | Value |
|---|---|
| Environment tier | Web server environment |
| Application name | Smartinvoice-api |
| Environment name | Smartinvoice-api-env |
| Platform | Docker |
| Platform branch | Docker running on 64bit Amazon Linux 2023 |
| Application code | Sample application (CI/CD will deploy later) |
| Presets | Single instance |

| Field | Value |
|---|---|
| Service role | aws-elasticbeanstalk-service-role |
| EC2 instance profile | aws-elasticbeanstalk-ec2-role |

| Field | Value |
|---|---|
| VPC | smartinvoice-vpc |
| Public IP address | ❌ Do NOT enable |
| Instance subnets | Private subnets (1a + 1b) |

| Field | Value |
|---|---|
| IMDSv1 | ✅ Disable |
| EC2 security groups | smartinvoice-backend-sg |
| Environment type | Load balanced |
| Instance type | t3.micro |
| Scaling Min / Max | 2 / 2 |

Basic (or Enhanced for detailed metrics)Click Submit and wait 5–10 minutes for the environment to be provisioned.
After the environment is ready, this file is used by GitHub Actions to deploy code from ECR:
{
"AWSEBDockerrunVersion": "1",
"Image": {
"Name": "<ACCOUNT_ID>.dkr.ecr.ap-southeast-1.amazonaws.com/smartinvoice-backend:latest",
"Update": "true"
},
"Ports": [
{
"ContainerPort": 8080,
"HostPort": 80
}
]
}