ECS containers produce logs through the log driver configured in the container definition. This module’s container-definition sub-module supports two logging backends: CloudWatch Logs and FireLens (FluentBit/Fluentd).
How log groups are managed
When CloudWatch logging is enabled, ECS will create a log group automatically if one does not exist. The problem with this approach is that the log group is created outside of Terraform: it cannot be tagged, its retention period cannot be set, it will not be deleted when you destroy the stack, and it cannot be encrypted with a customer-managed KMS key.
To address this, the container-definition module creates the CloudWatch log group on your behalf by default. This means you get full Terraform control over the log group lifecycle.
The default retention period for log groups created by the container-definition module is 14 days. The cluster-level log group defaults to 90 days. Override both with cloudwatch_log_group_retention_in_days.
The four logging scenarios
Set enable_cloudwatch_logging = false to disable all logging for the container. No log group is created and no log driver is configured.module "ecs_service" {
source = "terraform-aws-modules/ecs/aws//modules/service"
# ... omitted for brevity
container_definitions = {
default = {
enable_cloudwatch_logging = false
# ...
}
}
}
Use this when:
- The container writes logs to a file or stdout that another sidecar collects.
- You want zero logging overhead for non-critical containers.
Set create_cloudwatch_log_group = false to use CloudWatch logging without Terraform managing the log group. ECS creates the log group automatically when the first task starts.module "ecs_service" {
source = "terraform-aws-modules/ecs/aws//modules/service"
# ... omitted for brevity
container_definitions = {
default = {
create_cloudwatch_log_group = false
# ...
}
}
}
Use this when:
- You manage the log group outside of this module (for example, with a shared log group for multiple services).
- You don’t need Terraform to control retention, encryption, or tagging of the log group.
Log groups created by ECS are not deleted when you destroy the Terraform stack. They also cannot be tagged or encrypted at creation time.
This is the default behavior. The module creates and manages the CloudWatch log group for you.module "ecs_service" {
source = "terraform-aws-modules/ecs/aws//modules/service"
# ... omitted for brevity
container_definitions = {
default = {
# No logging config needed — Terraform-managed CloudWatch is the default
}
}
}
You can customize the log group with additional inputs on the container definition:container_definitions = {
(local.container_name) = {
enable_cloudwatch_logging = true
create_cloudwatch_log_group = true
cloudwatch_log_group_name = "/aws/ecs/${local.name}/${local.container_name}"
cloudwatch_log_group_retention_in_days = 7
}
}
FireLens routes container logs through a FluentBit (or Fluentd) sidecar to any supported destination — S3, Kinesis, Elasticsearch, third-party SaaS tools, and more.You need two container definitions: one for the FluentBit sidecar and one for your application.data "aws_ssm_parameter" "fluentbit" {
name = "/aws/service/aws-for-fluent-bit/stable"
}
module "ecs_service" {
source = "terraform-aws-modules/ecs/aws//modules/service"
# ... omitted for brevity
container_definitions = {
# FluentBit sidecar is required for Firelens
fluent-bit = {
image = data.aws_ssm_parameter.fluentbit.value
firelensConfiguration = {
type = "fluentbit"
}
# ...
}
default = {
dependsOn = [{
containerName = "fluent-bit"
condition = "START"
}]
enable_cloudwatch_logging = false
logConfiguration = {
logDriver = "awsfirelens"
options = {
# ...
}
}
# ...
}
}
}
The application container must:
- Set
enable_cloudwatch_logging = false to prevent the module from also configuring CloudWatch.
- Add a
dependsOn condition so the app container waits for FluentBit to start.
- Set
logDriver = "awsfirelens" in logConfiguration.
Log group configuration options
When the module manages the CloudWatch log group (create_cloudwatch_log_group = true), you can control its configuration through container definition inputs:
| Input | Default | Description |
|---|
cloudwatch_log_group_name | Auto-generated | Custom name for the log group. |
cloudwatch_log_group_retention_in_days | 14 | Days to retain log events. Set to 0 for indefinite retention. |
cloudwatch_log_group_kms_key_id | null | ARN of a KMS key for encrypting log data at rest. |
cloudwatch_log_group_class | null | Log class: STANDARD or INFREQUENT_ACCESS. |
KMS encryption example
container_definitions = {
default = {
cloudwatch_log_group_kms_key_id = aws_kms_key.logs.arn
cloudwatch_log_group_retention_in_days = 30
}
}
If you provide a KMS key, ensure the key policy grants CloudWatch Logs the kms:GenerateDataKey* and kms:Decrypt permissions. Without the correct key policy, log group creation will fail.
FireLens with Kinesis Firehose
The service module README shows a complete FireLens example forwarding to Kinesis Firehose:
module "ecs_service" {
source = "terraform-aws-modules/ecs/aws//modules/service"
name = "example"
cluster_arn = "arn:aws:ecs:us-west-2:123456789012:cluster/default"
cpu = 1024
memory = 4096
container_definitions = {
fluent-bit = {
cpu = 512
memory = 1024
essential = true
image = "906394416424.dkr.ecr.us-west-2.amazonaws.com/aws-for-fluent-bit:stable"
firelensConfiguration = {
type = "fluentbit"
}
memoryReservation = 50
}
ecs-sample = {
cpu = 512
memory = 1024
essential = true
image = "public.ecr.aws/aws-containers/ecsdemo-frontend:776fd50"
portMappings = [
{
name = "ecs-sample"
containerPort = 80
protocol = "tcp"
}
]
readonlyRootFilesystem = false
dependsOn = [{
containerName = "fluent-bit"
condition = "START"
}]
enable_cloudwatch_logging = false
logConfiguration = {
logDriver = "awsfirelens"
options = {
Name = "firehose"
region = "eu-west-1"
delivery_stream = "my-stream"
log-driver-buffer-limit = "2097152"
}
}
memoryReservation = 100
}
}
subnet_ids = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"]
}
Retrieve the latest stable FluentBit image from SSM Parameter Store rather than hardcoding the image tag:data "aws_ssm_parameter" "fluentbit" {
name = "/aws/service/aws-for-fluent-bit/stable"
}
Then reference it as nonsensitive(data.aws_ssm_parameter.fluentbit.value) in the container definition.
For more FireLens configuration examples, see the FireLens examples repository.