AWS → STACKIT · LOGGING AND MONITORING

CloudWatch logging and monitoring on STACKIT

CloudWatch bundles log groups, log-derived metrics, alarms and notifications. On STACKIT these become a Logs instance, an Observability instance with alert rules, and receivers for e-mail. StackToIT drafts the instances and the alert group in Terraform and lists, file by file, what a person still writes: the log labels, the alert expressions and the mail relay.

The migration in seven steps

The order follows the estate that StackToIT assessed for this page: two log groups, one metric filter, two alarms, one SNS topic with an e-mail subscription and one CloudWatch board.

  1. List what the estate really uses

    Read the log groups with their retention, the metric filters, the alarms and the topics behind them. A metric filter or alarm that nobody reads is cheaper to retire than to rebuild. The assessment lists each log group, alarm and topic and the board as its own unit; the metric filter and the e-mail subscription sit inside the units they belong to.

  2. Create the Logs instances

    Log groups that keep their logs for the same number of days share one Logs instance. Retention is a setting of the instance, not of a group, so the 30-day group and the 365-day group landed on two instances.

  3. Choose a label convention and a write token

    STACKIT Logs has no log group or stream object. The group name survives only as a label that your shipper sends. Create a write access token per instance and point the shipper at the instance’s ingest address (Loki API or OpenTelemetry).

  4. Rebuild log-derived metrics

    A Logs instance has no metric filter. Turn the filter into a log alert rule in Observability, or let the application publish the metric itself.

  5. Write the alert expressions

    The alert group keeps each alarm’s name and its “for” duration. The expression is a required variable, because no CloudWatch metric is known to exist in your Observability instance.

  6. Set up receivers

    The e-mail subscription of the SNS topic becomes a receiver and a route in the alert configuration of the Observability instance. The address and the SMTP relay are variables you fill in.

  7. Run both in parallel, then retire

    Ship logs to both targets, compare alert behaviour on real incidents, and switch off the CloudWatch side after acceptance.

Log groups, a metric filter and two alarms

The input was written for this page and assessed by StackToIT on 4 November 2026. Left: the input from the audit log group to the latency alarm. Right: blocks of the STACKIT Terraform the engine wrote, with the review comments that sit above each block in the file left out.

Example inputmain.tf
resource "aws_cloudwatch_log_group" "audit" {
  name              = "/acme/audit"
  retention_in_days = 365
}

resource "aws_cloudwatch_log_metric_filter" "errors" {
  name           = "orders-api-errors"
  log_group_name = aws_cloudwatch_log_group.api.name
  pattern        = "ERROR"

  metric_transformation {
    name      = "OrdersApiErrors"
    namespace = "Acme/Orders"
    value     = "1"
  }
}

resource "aws_sns_topic" "oncall" {
  name = "acme-oncall"
}

resource "aws_sns_topic_subscription" "oncall_mail" {
  topic_arn = aws_sns_topic.oncall.arn
  protocol  = "email"
  endpoint  = "oncall@example.com"
}

resource "aws_cloudwatch_metric_alarm" "errors_high" {
  alarm_name          = "orders-api-errors-high"
  namespace           = "Acme/Orders"
  metric_name         = "OrdersApiErrors"
  statistic           = "Sum"
  period              = 300
  evaluation_periods  = 1
  threshold           = 10
  comparison_operator = "GreaterThanThreshold"
  alarm_actions       = [aws_sns_topic.oncall.arn]
}

resource "aws_cloudwatch_metric_alarm" "latency_high" {
  alarm_name          = "orders-alb-latency-high"
  namespace           = "AWS/ApplicationELB"
  metric_name         = "TargetResponseTime"
  statistic           = "Average"
  period              = 60
  evaluation_periods  = 5
  threshold           = 1.5
  comparison_operator = "GreaterThanThreshold"
  alarm_actions       = [aws_sns_topic.oncall.arn]
}
STACKIT Terraform by StackToITterraform/observability.tf
resource "stackit_logs_instance" "logs_retention_180d" {
  project_id     = var.project_id
  display_name   = "logs-retention-180d"
  retention_days = 180
}

resource "stackit_observability_alertgroup" "migrated" {
  project_id  = var.project_id
  instance_id = stackit_observability_instance.migrated.instance_id
  name        = "migrated-alarms"
  rules = [{
    alert      = "errors-high"
    expression = var.alarm_errors_high_expression
    labels = {
      source_alarm    = "aws_cloudwatch_metric_alarm.errors_high"
      notify_receiver = "sns-oncall"
    }
    annotations = {
      summary = "orders-api-errors-high"
    }
    }, {
    alert      = "latency-high"
    expression = var.alarm_latency_high_expression
    for        = "240s"
    labels = {
      source_alarm    = "aws_cloudwatch_metric_alarm.latency_high"
      notify_receiver = "sns-oncall"
    }
    annotations = {
      summary = "orders-alb-latency-high"
    }
  }]
}
  • 365 days become 180

    The audit group keeps its logs for 365 days. A STACKIT Logs instance keeps at most 180 days, so the draft sets 180 and records a decision: keep it at that, or also stream the logs to long-term storage.

  • The “for” duration is derived, the expression is not

    The latency alarm evaluates five one-minute periods. The draft takes (periods minus one) × period, so the rule carries for = 240s; the error alarm, with a single period, gets no for. Metric, comparison and threshold are not translated; they go into the variable you supply.

  • Labels route notifications

    Each rule carries a notify_receiver label that the route of the Observability instance matches against the e-mail receiver.

What each CloudWatch resource became

Read from the run’s assessment and Terraform. All six units are classed RECONFIGURE; no unit was left unmapped.

CloudWatch resourceSTACKIT targetWhat a person still does
aws_cloudwatch_log_group (two)STACKIT Logs instance, one per retention periodChoose the label, create the write token and access list, point the shipper at the ingest address
aws_cloudwatch_log_metric_filterNo equivalent in Logs; named as not carriedRebuild as a log alert rule or as an application metric
aws_cloudwatch_metric_alarm (two)Rules in one Observability alert groupWrite the PromQL expression for each rule against metrics that exist in your instance
aws_sns_topic and e-mail subscriptionReceiver and route in the alert configuration of the Observability instanceEnter the recipient address and the SMTP relay; other subscription types need their own receiver
CloudWatch board resourceGrafana in the Observability instance (confidence LOW)Rebuild the panels in Grafana; the board body is not carried

Both the metric filter and the SNS subscription are absorbed into the units they belong to. They do not appear as separate units, but the draft names what is not carried for each.

The Observability instance with its receiver

The receiver for the SNS e-mail subscription, as written in the same file. The recipient address is a variable: the engine does not copy a personal address from the source.

STACKIT Terraform by StackToITterraform/observability.tf
resource "stackit_observability_instance" "migrated" {
  project_id = var.project_id
  name       = "migrated-observability"
  plan_name  = var.observability_plan_name
  alert_config = {
    receivers = [{
      name = "default"
      }, {
      name = "sns-oncall"
      email_configs = [{
        to            = var.alert_mail_to_oncall_mail
        from          = var.alert_mail_from
        smart_host    = var.alert_mail_smart_host
        send_resolved = false
      }]
    }]
    route = {
      receiver = "default"
      routes = [{
        receiver = "sns-oncall"
        matchers = ["notify_receiver=\"sns-oncall\""]
      }]
    }
  }
}

Decisions your architects own

  • Retention beyond 180 days

    Audit logs older than 180 days exist only if you archive them, for example in a separate Object Storage bucket. STACKIT’s own FAQ recommends that cold-storage approach for long retention.

  • Label key for the group name

    The plan chooses no label key and reconfigures no producer. Pick one convention for all log sources before the first line is shipped.

  • Alert semantics

    CloudWatch evaluates statistic, period and datapoints. A PromQL rule evaluates differently, so check each rule against a real incident, not only against a syntax check.

  • Mail relay and credentials

    The draft writes no SMTP login, and it generates no Logs access token, because the token is a credential that ends up in the state. Decide where both live.

  • Boards and history

    Panels are rebuilt by hand. Historic logs and metrics are not copied by the draft; decide whether anyone needs them after cutover.

  • Percentile and anomaly alarms

    This estate has none. If yours does, check that Prometheus can express them before you promise parity.

Questions operations teams ask

Can I keep my CloudWatch agent?

No. The agent sends to CloudWatch. On STACKIT you ship logs with a collector that speaks the Loki API or OpenTelemetry. STACKIT’s Logs documentation has how-tos for Vector, Fluent Bit, the OpenTelemetry Collector and Grafana Alloy.

Do metric filters carry over?

No. A CloudWatch metric filter turns matching log events into a metric, and it only counts events after the filter exists. STACKIT Logs has no such object, so the run lists it as not carried and points to a log alert rule or an application metric.

How long can STACKIT Logs keep my logs?

The STACKIT Logs FAQ states that individual retention beyond 180 days is not supported. For longer periods you stream a copy to cheaper long-term storage in parallel.

Does StackToIT move the existing log history?

No. StackToIT changes nothing in any cloud and copies no data. It drafts the targets and writes the steps your team carries out.

Sources

Your next move: STACKIT.

Tell us about your estate. We show you what StackToIT delivers for it: target architecture, Terraform artifacts and a structured migration plan.