Docker on AWS: Deploying Containerized Apps Using ECS and Fargate
In today’s fast-paced development environment, containerization has become the go-to solution for building, testing, and deploying applications consistently across environments.
Docker makes it easy to package your application and its dependencies into a single container, ensuring it runs reliably no matter where it’s deployed. But local development is just the start.
To truly scale and manage containers in production, AWS provides ECS (Elastic Container Service) and Fargate, a serverless compute engine for containers.
This guide walks you through moving a Dockerized app from your local machine to AWS using ECS and Fargate, with hands-on examples and practical insights.
Why Docker + AWS?
Think of Docker as your personal workspace. You can build and run your app with all dependencies in a predictable environment.
AWS ECS and Fargate, on the other hand, are like moving your app from your desk into a fully managed office building:
- ECS (Elastic Container Service): A service to orchestrate your Docker containers at scale. You can define tasks, services, and clusters.
- Fargate: Runs containers without requiring you to manage servers or clusters. It’s a serverless way to run Docker containers on AWS.
This combination allows developers to focus on code while AWS handles infrastructure scaling, security, and orchestration.
Step 1: Dockerize Your App
Before deploying to AWS, you need a Dockerized application.
Let’s use a simple Node.js app as an example.
app.js
const express = require('express');
const app = express();
const PORT = 3000;
app.get('/', (req, res) => {
res.send('Hello from Docker on AWS!');
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});package.json
{
"name": "docker-aws-ecs",
"version": "1.0.0",
"main": "app.js",
"dependencies": {
"express": "^4.18.2"
},
"scripts": {
"start": "node app.js"
}
}Dockerfile
# Use official Node.js image
FROM node:18
# Set working directory
WORKDIR /usr/src/app
# Copy package files and install dependencies
COPY package*.json ./
RUN npm install
# Copy app source code
COPY . .
# Expose the app port
EXPOSE 3000
# Start the app
CMD ["npm", "start"]Now, build and test locally:
docker build -t docker-aws-ecs .
docker run -p 3000:3000 docker-aws-ecsVisit http://localhost:3000 to see your app in action.
Step 2: Push Your Image to AWS ECR
AWS Elastic Container Registry (ECR) is a fully managed Docker container registry.
Get Adekola Olawale’s stories in your inbox
Join Medium for free to get updates from this writer.
It’s like a warehouse where your container images are stored before deployment.
- Create an ECR repository
aws ecr create-repository --repository-name docker-aws-ecs2. Authenticate Docker to ECR
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com3. Tag and push the image
docker tag docker-aws-ecs:latest <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/docker-aws-ecs:latest
docker push <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/docker-aws-ecs:latestYour Docker image is now in the cloud, ready for deployment.
Step 3: Create an ECS Cluster
An ECS cluster is like a container playground. It’s where your tasks (containers) will run.
aws ecs create-cluster --cluster-name docker-clusterYou can verify the cluster:
aws ecs describe-clusters --clusters docker-clusterStep 4: Define a Task Definition
A task definition is like a blueprint for your container. It tells ECS which Docker image to use, how much CPU/memory to allocate, and which ports to expose.
{
"family": "docker-task",
"networkMode": "awsvpc",
"containerDefinitions": [
{
"name": "docker-aws-app",
"image": "<AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/docker-aws-ecs:latest",
"memory": 512,
"cpu": 256,
"essential": true,
"portMappings": [
{
"containerPort": 3000,
"hostPort": 3000
}
]
}
],
"requiresCompatibilities": ["FARGATE"],
"cpu": "256",
"memory": "512",
"executionRoleArn": "arn:aws:iam::<AWS_ACCOUNT_ID>:role/ecsTaskExecutionRole"
}Save as task-definition.json and register it:
aws ecs register-task-definition --cli-input-json file://task-definition.jsonStep 5: Deploy Using Fargate
With Fargate, you don’t worry about EC2 instances. It runs your containers directly.
- Create a VPC and Subnet (if not already set up). ECS requires networking for Fargate tasks.
- Run a Fargate service
aws ecs create-service \
--cluster docker-cluster \
--service-name docker-service \
--task-definition docker-task \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[<SUBNET_ID>],securityGroups=[<SECURITY_GROUP_ID>],assignPublicIp=ENABLED}" \
--desired-count 1Step 6: Access Your App
Once deployed, Fargate provides a public IP through the attached subnet. Navigate to the IP and port 3000 in your browser, and you should see your Node.js app running live in AWS.
Step 7: Bonus Tips for Production
- Use Load Balancers: For scaling and high availability, place your Fargate service behind an Application Load Balancer.
- Enable Auto Scaling: ECS supports auto-scaling based on CPU/memory usage.
- Environment Variables: Use ECS task definitions to inject secrets and configuration.
- CI/CD Integration: Automate deployments using GitHub Actions or AWS CodePipeline.
Analogy Recap
Think of your deployment workflow like a restaurant chain:
- Docker locally: Your kitchen at home where you test recipes.
- ECR: The central pantry where ingredients (images) are stored.
- ECS/Fargate: Managed restaurants where customers (users) enjoy your dish without worrying about kitchen logistics.
By separating development, storage, and deployment, you get consistency, scalability, and reliability.
Conclusion
Moving from local Docker development to AWS ECS with Fargate allows developers to deploy containerized applications without managing servers manually.
This workflow ensures your apps are scalable, resilient, and production-ready.
By following this guide, you’ve learned how to:
- Dockerize a Node.js app.
- Push your image to AWS ECR.
- Create an ECS cluster.
- Register a task definition.
- Deploy your container using Fargate.
- Access your app in production.
This setup forms the foundation for more advanced deployments, including CI/CD pipelines, microservices, and multi-region deployments.











