Push a Docker image to Amazon ECR and run it on ECS with Fargate, step by step, with CLI commands and common errors."
tags: aws, docker, devops, containers
Running a container on your laptop is easy. Running it reliably in the cloud means you need somewhere to store images and something to run, restart, and scale them. On AWS, that's Amazon ECR and Amazon ECS.
In this post I'll explain what each does and walk through deploying a containerized app on Fargate, so there are no servers to manage.
ECR vs ECS in one minute
Amazon ECR (Elastic Container Registry) is a private, managed registry for your container images. Think of it as a Docker Hub that lives inside your AWS account, with IAM-based access control, vulnerability scanning, and lifecycle policies.
Amazon ECS (Elastic Container Service) is the orchestrator. You describe what to run, and ECS places it, restarts it if it fails, and keeps the desired number of copies running.
| Concept | What it is |
|---|---|
| Cluster | Logical grouping of tasks and services |
| Task definition | Blueprint: image, CPU, memory, ports, env vars, roles |
| Task | A running instance of a task definition |
| Service | Keeps N tasks running and replaces failed ones |
| Fargate | Serverless compute for containers, no EC2 to manage |
The flow looks like this:
docker build → docker push → ECR (stores image)
↓
ECS task definition points to the image URI
↓
ECS service runs tasks on Fargate
Prerequisites
- An AWS account
- AWS CLI configured (
aws configure) - Docker installed
- A small app with a
Dockerfile(Nginx or Node.js is fine)
Step 1: Create the ECR repository
aws ecr create-repository \
--repository-name my-app \
--region ap-south-1
Copy the repositoryUri from the output. It looks like <account-id>.dkr.ecr.ap-south-1.amazonaws.com/my-app.
Step 2: Build, tag, and push the image
# Build
docker build -t my-app .
# Authenticate Docker to ECR
aws ecr get-login-password --region ap-south-1 | \
docker login --username AWS --password-stdin \
<account-id>.dkr.ecr.ap-south-1.amazonaws.com
# Tag and push
docker tag my-app:latest <account-id>.dkr.ecr.ap-south-1.amazonaws.com/my-app:latest
docker push <account-id>.dkr.ecr.ap-south-1.amazonaws.com/my-app:latest
Check the ECR console and you should see the image with the latest tag.
Step 3: Create an ECS cluster
In the ECS console, choose Clusters → Create cluster, name it my-app-cluster, keep AWS Fargate (serverless) selected, and create it.
Step 4: Create the task definition
- Task definitions → Create new task definition
- Name:
my-app-task, launch type: AWS Fargate - Size: for example 0.5 vCPU / 1 GB for a small app
-
Task execution role:
ecsTaskExecutionRole - Add a container with your ECR image URI and container port (for example
80) - Leave CloudWatch logging enabled
Step 5: Run it as a service
- Open the cluster → Services → Create
- Launch type: Fargate, pick your task definition
- Desired tasks:
1(use2for availability) - Choose your VPC and subnets
- Use a security group that allows inbound traffic on the container port
- Create the service
Step 6: Test it
When the task shows RUNNING, open it, copy the public IP, and visit http://<public-ip>. Your container is live.
Task role vs task execution role
This trips up a lot of beginners:
- Task execution role: used by ECS itself to pull the image from ECR and write logs to CloudWatch
- Task role: used by your app code inside the container to call AWS services (for example, reading from S3)
Keep both as narrow as possible.
Troubleshooting
| Problem | Likely cause |
|---|---|
CannotPullContainerError |
Execution role missing permissions, wrong image URI, or no network path to ECR (needs a public IP or NAT/VPC endpoints) |
| Task keeps stopping | App crashes on startup. Check CloudWatch logs and the exit code |
| Can't reach the app | Security group doesn't allow the container port, or the task has no public IP |
| Service stuck at 0 running tasks | Check the service Events tab for the exact error |
Best practices
- Don't deploy
latestto production. Tag images with a version or commit hash - Turn on scan on push in ECR
- Add lifecycle policies to expire old untagged images
- Put an Application Load Balancer in front and run tasks in private subnets for production
- Use least-privilege IAM roles
- Store secrets in Secrets Manager or SSM Parameter Store, never as plain text in the task definition
Wrapping up
ECR gives your images a secure home, and ECS on Fargate runs them without any server management. Once images, task definitions, and services click, you have the foundation for production-ready container workloads on AWS. A good next step is adding an ALB and auto scaling, then automating the build-and-deploy flow with CI/CD.
If you found this useful, let me know in the comments what you'd like me to cover next.
Connect with me
- LinkedIn: vishalpatil-devops-aws
- dev.to: @vishal_patil_aws
- GitHub: VishalPatil-DevOps-AWS
Top comments (0)