Deploying Docker containers on Amazon Web Services Elastic Container Service (AWS ECS) is a powerful way to run and manage containerized applications at scale. This guide expands on the essential steps, covering architecture decisions, security, monitoring, and production best practices. Whether you are new to ECS or looking to refine your deployment pipeline, you will find actionable details to get your containers running efficiently on AWS.

Understanding AWS ECS

AWS ECS is a highly scalable, high-performance container orchestration service that supports Docker containers. It allows you to run applications on a managed cluster of EC2 instances or using AWS Fargate, a serverless compute engine. ECS simplifies deployment, scaling, and management of containerized apps by handling the underlying infrastructure and providing integration with other AWS services.

Fargate vs. EC2 Launch Types

When you create an ECS cluster, you must choose between two launch types: Fargate and EC2.

  • AWS Fargate – A serverless compute engine where you specify CPU and memory requirements, and AWS manages the underlying servers. You pay only for the resources consumed. Fargate is ideal for teams that want to avoid managing servers and need quick scaling.
  • EC2 Launch Type – You manage a fleet of EC2 instances that host your containers. This offers more control over instance types, pricing models (e.g., Spot Instances), and the ability to run complex workloads with custom networking or GPU support. EC2 is suitable for cost optimization at scale or when you need persistent storage or specialized instance capabilities.

Your choice impacts cost, operational overhead, and flexibility. For most new projects, Fargate provides a simpler path, while EC2 may be better for high‑throughput or GPU‑intensive workloads.

Prerequisites

  • An AWS account with appropriate permissions (administrative or ECS‑specific policies).
  • Docker installed locally for building and testing images.
  • A Docker image of your application uploaded to Amazon Elastic Container Registry (ECR) or a public registry like Docker Hub.
  • Basic knowledge of AWS CLI and Docker commands. Install the AWS CLI and configure it with your credentials.
  • Familiarity with IAM roles and security groups to define container permissions and network access.

Step 1: Push Docker Image to Amazon ECR

Amazon ECR is a fully managed container registry that works seamlessly with ECS. Using ECR avoids public registry limits and improves security by keeping images within your AWS account.

Create an ECR Repository

Navigate to the ECR console and create a repository. Choose a name that matches your application (e.g., my-app). Note the repository URI, which looks like <account-id>.dkr.ecr.<region>.amazonaws.com/my-app.

Authenticate Docker to ECR

Run the following command to retrieve an authentication token and pass it to Docker:

aws ecr get-login-password --region your-region | docker login --username AWS --password-stdin your-account-id.dkr.ecr.your-region.amazonaws.com

This command logs your local Docker client into the private ECR registry. Ensure your AWS CLI has proper permissions to call ecr:GetAuthorizationToken.

Build and Tag the Docker Image

Build your image from a Dockerfile:

docker build -t your-repo-name .

Tag it to match your ECR repository:

docker tag your-repo-name:latest your-account-id.dkr.ecr.your-region.amazonaws.com/your-repo-name:latest

Push the Image to ECR

Finally, push the tagged image:

docker push your-account-id.dkr.ecr.your-region.amazonaws.com/your-repo-name:latest

After the push completes, your image is available in ECR. You can verify it in the AWS console under your repository. For production, consider using immutable image tags (e.g., Git commit SHA) instead of latest to enable rollbacks.

Step 2: Create an ECS Cluster

Log in to the AWS Management Console and navigate to ECS. Click on "Clusters" and then "Create Cluster".

  • If you choose Fargate, you only need to provide a cluster name and optionally enable Container Insights for monitoring. No EC2 instances are provisioned.
  • If you choose EC2 Linux + Networking, you must configure an EC2 instance type, desired capacity, key pair for SSH access, and an Auto Scaling group. The cluster will maintain a pool of EC2 instances that run your tasks.

After creation, the cluster appears in the console. You can manage tasks and services within it.

Step 3: Define a Task Definition

A task definition is a JSON template that describes how your containers should run. In ECS, create a new task definition and configure the following:

  • Container image URL – Use the ECR URI from Step 1.
  • Memory and CPU requirements – For Fargate, specify exact values (e.g., 512 CPU units, 1024 MB memory). For EC2, set resource limits.
  • Port mappings – Map container ports (e.g., 80) to host ports. For Fargate, the host port must be set to 0 (dynamic) or the same as container port.
  • Environment variables – Pass configuration such as database URLs or API keys. Alternatively, use a secrets manager (AWS Secrets Manager or SSM Parameter Store) for sensitive values.
  • Log collection – Configure the awslogs log driver to send container logs to CloudWatch Logs. Specify a log group name and region.

Save the task definition. You can create multiple revisions over time.

Step 4: Run a Service

A service ensures that a specified number of task instances are always running and automatically replaces failed tasks. In your ECS cluster, create a new service:

  • Launch type – Choose Fargate or EC2 to match your cluster.
  • Task definition and revision – Select the one created in Step 3.
  • Cluster VPC and subnets – Choose the VPC and at least two availability zones for high availability.
  • Security groups – Attach a security group that allows inbound traffic on the ports your container exposes.
  • Load balancing – Optionally attach an Application Load Balancer (ALB) to distribute traffic. This requires a target group and health check configuration.
  • Desired tasks – Set the number of copies (e.g., 2).

Review your settings and create the service. ECS will pull the container image, start tasks, and register them with the load balancer if configured.

Step 5: Access Your Application

Once your service is running, you can reach your application:

  • If you attached an ALB, use the DNS name of the load balancer (found in the EC2 > Load Balancers console). Ensure the ALB security group allows inbound HTTP/HTTPS traffic.
  • Without a load balancer, you can access tasks directly via the public IP of the EC2 instance (EC2 launch type) or via a Network Load Balancer. Fargate tasks do not have public IPs unless assigned in a public subnet.

Security groups must allow inbound traffic on the necessary ports (e.g., 80, 443). For health checks, configure the ALB to ping a path like /health.

Monitoring and Logging

AWS CloudWatch is the primary tool for monitoring ECS. Enable Container Insights on your cluster to collect metrics such as CPU, memory, and network utilization per task. To view container logs:

  • Ensure the task definition uses the awslogs log driver with a CloudWatch log group.
  • Navigate to CloudWatch > Log groups and select the group created for your service.
  • Use log streams to inspect stdout/stderr of each container.

Set up CloudWatch alarms for high CPU or memory to trigger auto‑scaling or alert your operations team. Also consider integrating with Amazon Managed Grafana or third‑party tools like Datadog for advanced dashboards.

For more details, refer to the AWS ECS CloudWatch documentation.

Auto Scaling

ECS supports both service auto scaling and cluster auto scaling (for EC2 launch type). Service auto scaling adjusts the desired count of tasks based on CloudWatch metrics or a target tracking policy (e.g., average CPU utilization at 60%).

  1. Go to your service in the ECS console.
  2. Under "Auto Scaling", click "Update" and configure the scaling policy.
  3. Define minimum and maximum tasks (e.g., 2–10).
  4. Choose a metric like CPUUtilization or MemoryUtilization.

For EC2 clusters, configure the Auto Scaling group to add or remove instances when the cluster runs out of resources. Use the ECS‑optimized AMI and associate a capacity provider to your service for automatic scaling.

CI/CD Integration

Automate your deployment pipeline using AWS CodePipeline, GitHub Actions, or Jenkins. A typical pipeline includes:

  1. Build the Docker image from your source code.
  2. Push the image to ECR with a unique tag (e.g., build number or commit SHA).
  3. Update the ECS service to use the new image. You can update the task definition revision and force a new deployment.

For example, using the AWS CLI:

aws ecs update-service --cluster my-cluster --service my-service --force-new-deployment

Integrate with GitHub Actions by using the aws-actions/amazon-ecs-deploy-task-definition action. This approach ensures repeatable, versioned deployments.

Security Best Practices

  • IAM roles – Use least‑privilege policies. Create an ECS task execution role that grants permissions to pull images from ECR and write logs. Create a task role for the application to access other AWS services (e.g., DynamoDB, S3).
  • Security groups – Restrict inbound traffic to only necessary ports and sources. For internal services, use private subnets without direct internet access.
  • Secrets management – Store sensitive data like database passwords in AWS Secrets Manager or SSM Parameter Store. Reference them in the task definition using the secrets array.
  • Network security – Deploy containers in VPCs with proper routing. Use VPC endpoints for ECR and CloudWatch to keep traffic within the AWS network.
  • Image scanning – Enable ECR scanning on push to detect vulnerabilities in your Docker images. Use Amazon Inspector for broader coverage.

For a comprehensive guide, see the AWS ECS Best Practices.

Troubleshooting Common Issues

Task Stuck in PENDING State

Check the service events tab in the ECS console. Common causes include insufficient CPU/memory in the cluster, unavailable container images, or missing IAM permissions. For Fargate, verify the task size is within the allowed combinations.

Containers Failing to Start

View the stopped task details and check the Stop Code reason. Common errors: port conflicts, invalid environment variables, or misconfigured health checks. CloudWatch logs can reveal application startup errors.

Load Balancer Health Checks Failing

Ensure the target group’s health check path returns HTTP 200. Verify the security group allows traffic from the load balancer’s subnet CIDR. For Fargate, the task must not have a host port mapping that conflicts.

Image Pull Failures

Verify the ECR repository URI is correct and the task execution role has ecr:GetDownloadUrlForLayer and ecr:BatchGetImage permissions. If using a private registry outside AWS, configure a secret for Docker credentials.

Conclusion

Deploying Docker containers on AWS ECS streamlines your application management and scaling. By understanding launch types, configuring task definitions, and integrating monitoring and CI/CD, you can build resilient production systems. Start small with Fargate, then adopt EC2 launch types when you need finer control or cost savings. Always apply security best practices and automate your pipeline to reduce manual errors. With these steps, your containers will run smoothly on AWS.

For further reading, consult the AWS ECS Developer Guide and the Docker documentation.