description: "A beginner-friendly, step-by-step guide to packaging an app with Docker, storing it in Amazon ECR, and running it on ECS Fargate with no servers to manage."
tags: aws, docker, devops, vishalcloudjourney
Docker is one of the most useful skills for anyone moving into cloud and DevOps. Running a container on your laptop is easy. The real question is how to run it reliably on AWS without managing servers.
In this article, you'll take a simple app from a Dockerfile to a running container on AWS, using these services:
- Docker to package the app
- Amazon ECR (Elastic Container Registry) to store the image
- Amazon ECS with AWS Fargate to run the container with no EC2 instances to manage
What You'll Need
- An AWS account (a Free Tier account is fine for learning)
- Docker installed on your machine
- AWS CLI installed and configured (
aws configure) - Node.js (optional, only to test the app outside Docker)
Step 1: Create a Simple App
Create a folder called my-docker-app with these files.
app.js
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hello from Docker running on AWS Fargate!');
});
app.listen(3000, '0.0.0.0', () => {
console.log('App running on port 3000');
});
package.json
{
"name": "my-docker-app",
"version": "1.0.0",
"main": "app.js",
"dependencies": {
"express": "^4.19.2"
}
}
Step 2: Write the Dockerfile
Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]
Why it's written this way:
-
alpinegives you a small image, so pulls are faster and the attack surface is smaller. - Copying
package*.jsonfirst lets Docker cache the dependency layer, so rebuilds are much faster.
Step 3: Build and Test Locally
docker build -t my-docker-app:v1 .
docker run -d -p 3000:3000 my-docker-app:v1
Open http://localhost:3000. You should see your message. Always test locally first, because it saves a lot of debugging time later.
Tip: If you're on an Apple Silicon Mac, build with
--platform linux/amd64so the image runs on standard Fargate.
Step 4: Push the Image to Amazon ECR
Create the repository:
aws ecr create-repository --repository-name my-docker-app --region ap-south-1
Authenticate Docker to ECR (replace <account-id> with your 12-digit AWS account ID):
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-docker-app:v1 <account-id>.dkr.ecr.ap-south-1.amazonaws.com/my-docker-app:v1
docker push <account-id>.dkr.ecr.ap-south-1.amazonaws.com/my-docker-app:v1
Go to ECR → Repositories → my-docker-app in the console. Your image should now be listed.
Step 5: Create an ECS Cluster
- Open Amazon ECS → Clusters → Create cluster
- Name it
docker-demo-cluster - Under infrastructure, keep AWS Fargate (serverless) selected
- Click Create
Step 6: Create a Task Definition
A task definition is the blueprint for your container.
- Go to ECS → Task definitions → Create new task definition
- Name:
my-docker-app-task - Launch type: AWS Fargate
- CPU / Memory: 0.25 vCPU / 0.5 GB (enough for this demo)
- Task execution role:
ecsTaskExecutionRole(let ECS create it if it doesn't exist) - Container details:
- Name:
my-docker-app - Image URI:
<account-id>.dkr.ecr.ap-south-1.amazonaws.com/my-docker-app:v1 - Container port:
3000
- Name:
- Click Create
The execution role is what allows Fargate to pull your image from ECR and send logs to CloudWatch.
Step 7: Run the Container as a Service
- Open your cluster and go to Services → Create
- Compute option: Launch type → Fargate
- Task definition:
my-docker-app-task - Desired tasks:
1 - Under Networking:
- Pick your VPC and a public subnet
- Create or select a security group that allows inbound TCP 3000 from your IP (or
0.0.0.0/0for a quick demo only) - Turn Public IP on
- Click Create
Wait a minute or two until the task status shows Running.
Step 8: Test It
- Open Cluster → Tasks → select your running task
- Copy the Public IP from the networking section
- Visit
http://<public-ip>:3000in your browser
You'll see: Hello from Docker running on AWS Fargate!
Your container is now running on AWS with no servers to patch or manage.
Troubleshooting Common Issues
| Problem | Likely Cause |
|---|---|
| Task stops immediately | Check the container logs in CloudWatch and confirm the CMD is correct |
| Can't pull the image | The execution role is missing ECR permissions, or the image URI has a typo |
| Page doesn't load | Security group isn't allowing port 3000, or the public IP isn't enabled |
| "exec format error" | Image built for the wrong CPU architecture (see the platform tip in Step 3) |
Best Practices
-
Don't use the
latesttag in production. Use versioned tags likev1andv2so you can roll back. - Turn on ECR image scanning to catch known vulnerabilities.
- Use least-privilege IAM roles for tasks.
- Put an Application Load Balancer in front for real workloads instead of exposing the task's public IP.
- Send logs to CloudWatch and set alarms on failures.
Clean Up to Avoid Charges
When you're done:
- Set the ECS service desired count to
0, then delete the service - Delete the cluster
- Delete the ECR repository (or the image)
- Delete any security groups you created for the demo
Conclusion
You've taken an application from source code to a running container on AWS using Docker, ECR, and ECS Fargate. This same pattern scales from a small demo to production workloads. Adding an ALB, auto scaling, and a CI/CD pipeline are natural next steps.
If you found this helpful, let me know which topic you'd like me to cover next.
Author: Vishal Patil
🔗 LinkedIn: vishalpatil-devops-aws
💻 GitHub: VishalPatil-DevOps-AWS
Top comments (0)