Introduction to ECS Monitoring
Amazon Elastic Container Service (ECS) abstracts away the underlying infrastructure so you can focus on running containerized applications. However, abstraction does not eliminate the need for observability. Monitoring ECS effectively means tracking the health, performance, and resource utilization of your clusters, services, and tasks โ and acting on that data before users are impacted.
ECS monitoring spans three pillars:
- Metrics โ numerical data emitted by ECS and your containers (CPU, memory, network, task counts, placement failures)
- Alarms โ automated thresholds that trigger notifications or actions when metrics breach acceptable ranges
- Dashboards โ visual representations that correlate metrics across services and clusters for quick diagnosis
This tutorial walks you through each pillar with practical, deployable examples. By the end, you will have a complete monitoring stack for ECS using CloudWatch, Container Insights, and CloudFormation-compatible alarm definitions.
Why ECS Monitoring Matters
Containers are ephemeral. A task can be replaced in seconds by a new one, making log-only debugging nearly impossible without aggregated metrics. Monitoring gives you:
- Early warning โ Catch memory leaks or CPU saturation before tasks crash
- Capacity planning โ Track actual utilization versus reserved capacity across the cluster
- Deployment validation โ Compare metrics before and after a rollout to detect regressions
- Cost optimization โ Identify over-provisioned services and right-size task definitions
- Compliance evidence โ Demonstrate uptime and response-time SLAs to auditors
Without monitoring, a production ECS cluster is a black box. The moment you have more than two services, you need structured visibility.
Part 1: Core ECS Metrics
Understanding the Metric Namespaces
ECS publishes metrics to two CloudWatch namespaces:
- AWS/ECS โ cluster-level and service-level metrics (task counts, CPU reservation, memory reservation, service RUNNING/PENDING counts)
- AWS/ECS/ContainerInsights โ per-task, per-container, and per-service performance metrics (requires enabling Container Insights)
The standard AWS/ECS namespace gives you the following key metrics without any extra configuration:
# Cluster-level metrics (dimensions: ClusterName)
CPUReservation # Percentage of total CPU reserved by tasks on the cluster
MemoryReservation # Percentage of total memory reserved by tasks on the cluster
CPUUtilization # Actual CPU usage aggregated across the cluster
MemoryUtilization # Actual memory usage aggregated across the cluster
ActiveTaskCount # Number of tasks in ACTIVE state
# Service-level metrics (dimensions: ClusterName, ServiceName)
CPUReservation # CPU reserved by tasks belonging to this service
MemoryReservation # Memory reserved by tasks belonging to this service
CPUUtilization # Actual CPU used by the service's tasks
MemoryUtilization # Actual memory used by the service's tasks
DesiredCount # Desired task count for the service
PendingCount # Tasks waiting to be placed or launched
RunningCount # Tasks currently running
Enabling Container Insights
Container Insights provides richer per-task metrics โ including per-container CPU, memory, network Rx/Tx, and storage. You enable it at the cluster level and it flows into the AWS/ECS/ContainerInsights namespace. It can be turned on via the AWS CLI, console, or infrastructure-as-code.
Using the AWS CLI:
aws ecs update-cluster-settings \
--cluster my-production-cluster \
--settings name=containerInsights,value=enabled \
--region us-east-1
Using CloudFormation (relevant snippet for an ECS cluster resource):
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: my-production-cluster
ClusterSettings:
- Name: containerInsights
Value: enabled
Tags:
- Key: environment
Value: production
Once enabled, Container Insights populates these additional metrics in CloudWatch under AWS/ECS/ContainerInsights with dimensions including ClusterName, ServiceName, TaskId, and ContainerName:
# Per-container metrics available with Container Insights
cpu_percent # CPU utilization percentage per container
memory_percent # Memory utilization percentage per container
network_rate_bytes # Network throughput in bytes per second
network_errors # Network error count
storage_bytes # Ephemeral storage usage in bytes
task_count # Number of tasks in a given state per service
Part 2: Querying Metrics with CloudWatch CLI
Before building alarms, you need to know how to query raw metric data. Here are practical examples using the AWS CLI.
Fetching CPU Utilization for a Service (Last 1 Hour)
aws cloudwatch get-metric-statistics \
--namespace AWS/ECS \
--metric-name CPUUtilization \
--dimensions Name=ClusterName,Value=production Name=ServiceName,Value=web-api \
--start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
--period 300 \
--statistics Average Maximum \
--region us-east-1
Fetching Memory Utilization with Container Insights
aws cloudwatch get-metric-statistics \
--namespace AWS/ECS/ContainerInsights \
--metric-name memory_percent \
--dimensions Name=ClusterName,Value=production Name=ServiceName,Value=web-api \
--start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
--period 300 \
--statistics Average p95 \
--region us-east-1
Checking Service Task Counts
aws cloudwatch get-metric-statistics \
--namespace AWS/ECS \
--metric-name RunningCount \
--dimensions Name=ClusterName,Value=production Name=ServiceName,Value=web-api \
--start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
--period 60 \
--statistics Minimum Maximum Average \
--region us-east-1
Part 3: CloudWatch Alarms for ECS
Why Alarms Are Critical
Metrics alone are passive. Alarms transform metrics into signals that demand attention. For ECS, the most impactful alarms detect:
- Service outages (RunningCount drops below desired)
- Resource saturation (CPU or memory utilization above 85% for sustained periods)
- Deployment failures (PendingCount spikes without converting to RunningCount)
- Container-level anomalies (per-container memory leak detection via Container Insights)
Alarm 1: Service Running Task Count Drop
This alarm triggers when the number of running tasks falls below the desired count for more than 2 evaluation periods โ indicating tasks are crashing or failing to start.
aws cloudwatch put-metric-alarm \
--alarm-name "web-api-task-count-drop" \
--alarm-description "Triggers when running tasks drop below desired count for 2 consecutive periods" \
--namespace AWS/ECS \
--metric-name RunningCount \
--dimensions Name=ClusterName,Value=production Name=ServiceName,Value=web-api \
--statistic Minimum \
--period 60 \
--evaluation-periods 2 \
--threshold 2 \
--comparison-operator LessThanThreshold \
--treat-missing-data breaching \
--alarm-actions arn:aws:sns:us-east-1:123456789012:ops-critical-topic \
--region us-east-1
Here the threshold is set to 2 because the service has a desired count of 3. If the minimum running count drops to 1 or 0 for two minutes, the alarm fires.
Alarm 2: High CPU Utilization on Service
This alarm detects sustained CPU pressure (over 85% for 5 minutes across 3 consecutive periods). It uses the Average statistic to smooth out transient spikes.
aws cloudwatch put-metric-alarm \
--alarm-name "web-api-high-cpu" \
--alarm-description "Alarm when service average CPU exceeds 85% for 15 minutes" \
--namespace AWS/ECS \
--metric-name CPUUtilization \
--dimensions Name=ClusterName,Value=production Name=ServiceName,Value=web-api \
--statistic Average \
--period 300 \
--evaluation-periods 3 \
--threshold 85 \
--comparison-operator GreaterThanThreshold \
--treat-missing-data notBreaching \
--alarm-actions arn:aws:sns:us-east-1:123456789012:ops-critical-topic \
--ok-actions arn:aws:sns:us-east-1:123456789012:ops-info-topic \
--region us-east-1
Alarm 3: Memory Utilization with Container Insights
Container Insights gives you per-container memory metrics, which is essential for catching memory leaks in individual containers before they crash the whole task.
aws cloudwatch put-metric-alarm \
--alarm-name "web-api-container-memory-leak" \
--alarm-description "Alarm when any web-api container exceeds 90% memory for 10 minutes" \
--namespace AWS/ECS/ContainerInsights \
--metric-name memory_percent \
--dimensions Name=ClusterName,Value=production Name=ServiceName,Value=web-api \
--statistic Maximum \
--period 300 \
--evaluation-periods 2 \
--threshold 90 \
--comparison-operator GreaterThanThreshold \
--treat-missing-data notBreaching \
--alarm-actions arn:aws:sns:us-east-1:123456789012:ops-critical-topic \
--region us-east-1
Alarm 4: Pending Task Count Spike (Deployment Stuck)
A sudden sustained increase in pending tasks without corresponding running tasks means deployments are stuck โ often due to insufficient cluster resources or placement constraints.
aws cloudwatch put-metric-alarm \
--alarm-name "web-api-deployment-stuck" \
--alarm-description "Triggers when pending tasks exceed 5 for more than 5 minutes" \
--namespace AWS/ECS \
--metric-name PendingCount \
--dimensions Name=ClusterName,Value=production Name=ServiceName,Value=web-api \
--statistic Average \
--period 300 \
--evaluation-periods 1 \
--threshold 5 \
--comparison-operator GreaterThanThreshold \
--treat-missing-data notBreaching \
--alarm-actions arn:aws:sns:us-east-1:123456789012:ops-warning-topic \
--region us-east-1
Composite Alarms for Reducing Noise
To avoid alarm fatigue, combine multiple metrics into a single composite alarm. For example, alert only when both CPU and memory are high, indicating real resource exhaustion rather than a temporary CPU spike from a single request.
# First, create individual metric alarms (they serve as inputs)
aws cloudwatch put-metric-alarm \
--alarm-name "web-api-cpu-warning" \
--namespace AWS/ECS \
--metric-name CPUUtilization \
--dimensions Name=ClusterName,Value=production Name=ServiceName,Value=web-api \
--statistic Average --period 300 --evaluation-periods 1 \
--threshold 80 --comparison-operator GreaterThanThreshold \
--treat-missing-data notBreaching \
--region us-east-1
aws cloudwatch put-metric-alarm \
--alarm-name "web-api-memory-warning" \
--namespace AWS/ECS \
--metric-name MemoryUtilization \
--dimensions Name=ClusterName,Value=production Name=ServiceName,Value=web-api \
--statistic Average --period 300 --evaluation-periods 1 \
--threshold 80 --comparison-operator GreaterThanThreshold \
--treat-missing-data notBreaching \
--region us-east-1
# Then create the composite alarm
aws cloudwatch put-composite-alarm \
--alarm-name "web-api-resource-crisis" \
--alarm-description "Composite: both CPU and memory above 80% simultaneously" \
--alarm-rule "ALARM(web-api-cpu-warning) AND ALARM(web-api-memory-warning)" \
--alarm-actions arn:aws:sns:us-east-1:123456789012:ops-critical-topic \
--region us-east-1
Part 4: CloudWatch Dashboards for ECS
Dashboard Architecture
A well-designed dashboard organizes widgets logically. For ECS, a typical layout includes:
- Row 1: Cluster Overview โ aggregate CPU, memory, active task count across the entire cluster
- Row 2: Service Health Grid โ one widget per critical service showing running count, CPU, memory
- Row 3: Deployment & Placement โ pending counts, placement failures, scaling activity
- Row 4: Container Insights Detail โ per-container metrics if Container Insights is enabled
Creating a Dashboard via CLI
The following creates a complete dashboard named "ECS-Production-Overview". Each widget is defined as a JSON object with properties for type, metrics, and layout.
aws cloudwatch put-dashboard \
--dashboard-name "ECS-Production-Overview" \
--dashboard-body '{
"widgets": [
{
"type": "text",
"x": 0, "y": 0, "width": 24, "height": 2,
"properties": {
"markdown": "## ECS Production Cluster Overview\n*Cluster: production | Region: us-east-1 | Last updated: $TIME*"
}
},
{
"type": "metric",
"x": 0, "y": 2, "width": 8, "height": 6,
"properties": {
"view": "timeSeries",
"stacked": false,
"title": "Cluster CPU Utilization",
"metrics": [
["AWS/ECS", "CPUUtilization", {"stat": "Average", "period": 60}],
["AWS/ECS", "CPUReservation", {"stat": "Average", "period": 60}]
],
"region": "us-east-1",
"yAxis": {"left": {"min": 0, "max": 100, "label": "Percent"}}
}
},
{
"type": "metric",
"x": 8, "y": 2, "width": 8, "height": 6,
"properties": {
"view": "timeSeries",
"stacked": false,
"title": "Cluster Memory Utilization",
"metrics": [
["AWS/ECS", "MemoryUtilization", {"stat": "Average", "period": 60}],
["AWS/ECS", "MemoryReservation", {"stat": "Average", "period": 60}]
],
"region": "us-east-1",
"yAxis": {"left": {"min": 0, "max": 100, "label": "Percent"}}
}
},
{
"type": "metric",
"x": 16, "y": 2, "width": 8, "height": 6,
"properties": {
"view": "timeSeries",
"stacked": false,
"title": "Active Task Count",
"metrics": [
["AWS/ECS", "ActiveTaskCount", {"stat": "Sum", "period": 60}]
],
"region": "us-east-1"
}
},
{
"type": "metric",
"x": 0, "y": 8, "width": 12, "height": 6,
"properties": {
"view": "timeSeries",
"stacked": false,
"title": "web-api Service Health",
"metrics": [
["AWS/ECS", "RunningCount", "ServiceName", "web-api", {"stat": "Minimum", "period": 60, "label": "Running Tasks"}],
["AWS/ECS", "DesiredCount", "ServiceName", "web-api", {"stat": "Average", "period": 60, "label": "Desired Tasks"}],
["AWS/ECS", "CPUUtilization", "ServiceName", "web-api", {"stat": "Average", "period": 300, "label": "CPU %", "yAxis": "right"}]
],
"region": "us-east-1",
"yAxis": {
"left": {"min": 0, "label": "Task Count"},
"right": {"min": 0, "max": 100, "label": "CPU %"}
}
}
},
{
"type": "metric",
"x": 12, "y": 8, "width": 12, "height": 6,
"properties": {
"view": "timeSeries",
"stacked": false,
"title": "worker-processor Service Health",
"metrics": [
["AWS/ECS", "RunningCount", "ServiceName", "worker-processor", {"stat": "Minimum", "period": 60, "label": "Running Tasks"}],
["AWS/ECS", "DesiredCount", "ServiceName", "worker-processor", {"stat": "Average", "period": 60, "label": "Desired Tasks"}],
["AWS/ECS", "MemoryUtilization", "ServiceName", "worker-processor", {"stat": "Average", "period": 300, "label": "Memory %", "yAxis": "right"}]
],
"region": "us-east-1",
"yAxis": {
"left": {"min": 0, "label": "Task Count"},
"right": {"min": 0, "max": 100, "label": "Memory %"}
}
}
},
{
"type": "metric",
"x": 0, "y": 14, "width": 12, "height": 6,
"properties": {
"view": "timeSeries",
"stacked": false,
"title": "Deployment Activity - Pending vs Running",
"metrics": [
["AWS/ECS", "PendingCount", "ServiceName", "web-api", {"stat": "Sum", "period": 60, "label": "web-api Pending"}],
["AWS/ECS", "RunningCount", "ServiceName", "web-api", {"stat": "Sum", "period": 60, "label": "web-api Running"}]
],
"region": "us-east-1"
}
},
{
"type": "metric",
"x": 12, "y": 14, "width": 12, "height": 6,
"properties": {
"view": "timeSeries",
"stacked": false,
"title": "Container Insights - web-api CPU per Container",
"metrics": [
["AWS/ECS/ContainerInsights", "cpu_percent", "ServiceName", "web-api", {"stat": "p95", "period": 60, "label": "p95 CPU"}],
["AWS/ECS/ContainerInsights", "cpu_percent", "ServiceName", "web-api", {"stat": "Average", "period": 60, "label": "Avg CPU"}]
],
"region": "us-east-1",
"yAxis": {"left": {"min": 0, "max": 100, "label": "Percent"}}
}
}
]
}' \
--region us-east-1
Building Dashboards with CloudFormation
For production infrastructure, dashboards should be defined as code. Below is a CloudFormation template fragment that creates the same dashboard and includes alarm widgets to show alarm status inline.
ECSDashboard:
Type: AWS::CloudWatch::Dashboard
Properties:
DashboardName: ECS-Production-Overview
DashboardBody: |
{
"widgets": [
{
"type": "text",
"x": 0, "y": 0, "width": 24, "height": 2,
"properties": {
"markdown": "## ECS Production Dashboard\n### Managed by CloudFormation"
}
},
{
"type": "metric",
"x": 0, "y": 2, "width": 6, "height": 6,
"properties": {
"view": "timeSeries",
"stacked": false,
"title": "web-api CPU (Avg)",
"metrics": [
["AWS/ECS", "CPUUtilization", "ServiceName", "web-api", "ClusterName", {"Fn::GetAtt": ["ECSCluster", "ClusterName"]}, {"stat": "Average", "period": 300}]
],
"region": {"Ref": "AWS::Region"},
"yAxis": {"left": {"min": 0, "max": 100}}
}
},
{
"type": "metric",
"x": 6, "y": 2, "width": 6, "height": 6,
"properties": {
"view": "timeSeries",
"stacked": false,
"title": "web-api Memory (Avg)",
"metrics": [
["AWS/ECS", "MemoryUtilization", "ServiceName", "web-api", "ClusterName", {"Fn::GetAtt": ["ECSCluster", "ClusterName"]}, {"stat": "Average", "period": 300}]
],
"region": {"Ref": "AWS::Region"},
"yAxis": {"left": {"min": 0, "max": 100}}
}
},
{
"type": "metric",
"x": 12, "y": 2, "width": 6, "height": 6,
"properties": {
"view": "timeSeries",
"stacked": false,
"title": "web-api Running Tasks",
"metrics": [
["AWS/ECS", "RunningCount", "ServiceName", "web-api", "ClusterName", {"Fn::GetAtt": ["ECSCluster", "ClusterName"]}, {"stat": "Minimum", "period": 60}],
["AWS/ECS", "DesiredCount", "ServiceName", "web-api", "ClusterName", {"Fn::GetAtt": ["ECSCluster", "ClusterName"]}, {"stat": "Average", "period": 60}]
],
"region": {"Ref": "AWS::Region"}
}
},
{
"type": "alarm",
"x": 18, "y": 2, "width": 6, "height": 6,
"properties": {
"title": "Active Alarms",
"alarms": [
{"Ref": "WebApiTaskCountAlarm"},
{"Ref": "WebApiHighCPUAlarm"},
{"Ref": "WebApiMemoryLeakAlarm"}
]
}
}
]
}
Part 5: Advanced Patterns
Metric Math for Derived Insights
CloudWatch Metric Math lets you compute ratios, differences, and aggregations across services. This is particularly useful for understanding cluster efficiency.
Example: Compute the ratio of actual CPU usage to reserved CPU. A low ratio means you are reserving more than you use โ a clear over-provisioning signal.
# Metric math expression for CPU efficiency ratio
# Usage รท Reservation, expressed as a percentage
{
"metrics": [
{
"expression": "(m1 / m2) * 100",
"label": "CPU Efficiency %",
"id": "e1"
},
{
"id": "m1",
"metricStat": {
"metric": {
"namespace": "AWS/ECS",
"metricName": "CPUUtilization",
"dimensions": {"ClusterName": "production"}
},
"stat": "Average",
"period": 300
},
"returnData": false
},
{
"id": "m2",
"metricStat": {
"metric": {
"namespace": "AWS/ECS",
"metricName": "CPUReservation",
"dimensions": {"ClusterName": "production"}
},
"stat": "Average",
"period": 300
},
"returnData": false
}
]
}
You can embed this expression directly in a dashboard widget or use it as the metric for an alarm. For instance, trigger an alarm when efficiency drops below 30% (meaning you reserve 100 CPU units but only use 30 โ wasting 70% of your reservation).
Cross-Service Aggregation with Metric Math
Sum the running task counts across multiple services to get a total cluster task count that excludes infrastructure tasks:
{
"metrics": [
{
"expression": "SUM(METRICS())",
"label": "Total Running Service Tasks",
"id": "e1"
},
{
"id": "m1",
"metricStat": {
"metric": {
"namespace": "AWS/ECS",
"metricName": "RunningCount",
"dimensions": {
"ClusterName": "production",
"ServiceName": "web-api"
}
},
"stat": "Sum",
"period": 60
},
"returnData": false
},
{
"id": "m2",
"metricStat": {
"metric": {
"namespace": "AWS/ECS",
"metricName": "RunningCount",
"dimensions": {
"ClusterName": "production",
"ServiceName": "worker-processor"
}
},
"stat": "Sum",
"period": 60
},
"returnData": false
},
{
"id": "m3",
"metricStat": {
"metric": {
"namespace": "AWS/ECS",
"metricName": "RunningCount",
"dimensions": {
"ClusterName": "production",
"ServiceName": "background-jobs"
}
},
"stat": "Sum",
"period": 60
},
"returnData": false
}
]
}
Anomaly Detection Alarms
Static thresholds work well for known limits, but they break when traffic patterns change (e.g., seasonal load). CloudWatch anomaly detection learns the metric's normal behavior and triggers when the value deviates significantly.
aws cloudwatch put-metric-alarm \
--alarm-name "web-api-cpu-anomaly" \
--alarm-description "Anomaly detection on web-api CPU utilization" \
--namespace AWS/ECS \
--metric-name CPUUtilization \
--dimensions Name=ClusterName,Value=production Name=ServiceName,Value=web-api \
--statistic Average \
--period 300 \
--evaluation-periods 2 \
--threshold 2 \
--comparison-operator GreaterThanUpperThreshold \
--treat-missing-data notBreaching \
--metrics '[
{
"Id": "base",
"MetricStat": {
"Metric": {
"Namespace": "AWS/ECS",
"MetricName": "CPUUtilization",
"Dimensions": [
{"Name": "ClusterName", "Value": "production"},
{"Name": "ServiceName", "Value": "web-api"}
]
},
"Stat": "Average",
"Period": 300
},
"ReturnData": true
},
{
"Id": "anomaly",
"MetricStat": {
"Metric": {
"Namespace": "AWS/ECS",
"MetricName": "CPUUtilization",
"Dimensions": [
{"Name": "ClusterName", "Value": "production"},
{"Name": "ServiceName", "Value": "web-api"}
]
},
"Stat": "Average",
"Period": 300
},
"ReturnData": true
},
{
"Id": "expected",
"Expression": "ANOMALY_DETECTION_BAND(base, 2)",
"Label": "Expected Range",
"ReturnData": true
}
]' \
--alarm-actions arn:aws:sns:us-east-1:123456789012:ops-critical-topic \
--region us-east-1
The band value of 2 means the alarm triggers when the metric falls outside 2 standard deviations from the learned baseline. Over time, the model adapts to new normal patterns.
Part 6: Best Practices for ECS Monitoring
1. Enable Container Insights on All Production Clusters
Container Insights costs roughly $0.25 per container instance per month (as of current pricing) and gives you per-container granularity that the base AWS/ECS namespace cannot provide. The return on investment is enormous when debugging container-specific issues.
2. Use Composite Alarms Over Multiple Single Alarms
Individual metric alarms create noise. A composite alarm like "CPU > 85% AND Memory > 85%" fires only when both conditions are true, dramatically reducing false positives during normal workload fluctuations.
3. Set Alarms on Reservation Metrics, Not Just Utilization
CPUReservation and MemoryReservation approaching 100% means no more tasks can be placed โ even if actual utilization is low. This is a placement risk alarm that should fire well before the cluster is full. Set a warning at 80% reservation and a critical alarm at 90%.
4. Monitor PendingCount During Deployments
A non-zero PendingCount that persists for more than a few minutes usually indicates a placement constraint violation or insufficient resources. Create a dedicated alarm for deployment windows with a shorter evaluation period (1 minute).
5. Dashboard Organization Matters
Group widgets by logical concern: cluster health, per-service health, deployments, and Container Insights detail. Use consistent y-axis ranges (0โ100 for percentages) so visual scanning is fast. Include alarm widgets so operators see alarm status alongside metrics.
6. Treat Missing Data Explicitly
Every alarm should have an explicit treat-missing-data setting. For RunningCount, treat missing as breaching (if the metric stops arriving, tasks are probably gone). For CPUUtilization, treat missing as notBreaching (transient gaps during deployments are normal).
7. Automate Dashboard and Alarm Creation
Manually creating dashboards via the console is not reproducible. Use CloudFormation, Terraform, or CDK to define dashboards and alarms alongside the ECS resources they monitor. This ensures every new environment gets the same monitoring baseline.
8. Integrate with Incident Management
Alarm actions should point to SNS topics that feed into your incident management tool (PagerDuty, OpsGenie, Slack). Include OK-actions to notify when alarms clear, so on-call staff know the issue resolved without checking.
9. Set Up Log-Based Metrics for Application-Level Signals
ECS metrics cover infrastructure, but application health requires log-based metrics. Use CloudWatch Logs metric filters on your container logs to extract error rates, latency percentiles, and business metrics. Example: create a metric that counts 5xx responses and alarm on it.
10. Regularly Review and Tune Thresholds
After any significant deployment or traffic pattern change, review alarm thresholds. A threshold that was correct at launch may be too sensitive (causing fatigue) or too lenient (missing real issues) six months later. Schedule a quarterly review of all ECS alarms.
Conclusion
Monitoring ECS is not a one-time setup โ it is an evolving practice that matures alongside your container infrastructure. Start with the fundamental metrics in the AWS/ECS namespace, enable Container Insights for per-container visibility, build targeted alarms that catch real failure modes, and surface everything in organized dashboards that your team actually uses during incidents. Use infrastructure-as-code to make your monitoring reproducible, leverage metric math for derived insights, and adopt anomaly detection for metrics with variable baselines. With the patterns and code examples in this tutorial, you have a complete blueprint for a production-grade ECS monitoring stack that reduces mean time to detect and resolve issues, prevents placement failures, and gives you confidence in every deployment.