โ† Back to DevBytes

Monitoring ECS: Metrics, Alarms, and Dashboards

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:

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:

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:

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:

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:

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.

๐Ÿš€ Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started โ€” $23.99/mo
โ† Back to all articles