DEV Community

Cover image for Monitoring Hetzner Cloud resources with AWS CloudWatch using Terraform
Kamran Biglari
Kamran Biglari

Posted on

Monitoring Hetzner Cloud resources with AWS CloudWatch using Terraform

In this tutorial, you will learn how to monitor your Hetzner Cloud infrastructure using AWS CloudWatch. This integration allows you to centralize your monitoring across both Hetzner and AWS resources in one unified dashboard.

By the end of this tutorial, you will have set up automated metric collection from your Hetzner Cloud Load Balancers and Servers, with all metrics automatically pushed to AWS CloudWatch at regular intervals. This is particularly useful if your organization already uses AWS CloudWatch for monitoring and wants to include Hetzner Cloud resources in the same observability platform.

We'll use a Terraform module that leverages AWS Step Functions to periodically fetch metrics from the Hetzner Cloud API and store them in CloudWatch. This serverless approach means there are no servers to manage, and you only pay for what you use.

Prerequisites

Before starting this tutorial, you should have:

  • An active Hetzner Cloud account with at least one Load Balancer or Server running
  • A Hetzner Cloud API token with read permissions (How to create an API token)
  • An AWS account with appropriate permissions to create IAM roles, Step Functions, EventBridge resources, and CloudWatch metrics
  • Terraform installed (version 1.0 or higher)
  • AWS credentials configured locally (via AWS CLI, environment variables, or AWS credentials file)
  • Basic familiarity with Terraform and Infrastructure as Code concepts

Step 1 - Understanding the Architecture

Before we begin the implementation, it's important to understand how this integration works.

The solution consists of several AWS components working together:

  1. AWS EventBridge Connection: Securely stores your Hetzner Cloud API token in AWS Secrets Manager
  2. AWS Step Functions State Machine: A serverless workflow that fetches metrics from the Hetzner Cloud API
  3. AWS EventBridge Scheduler: Triggers the Step Functions workflow at regular intervals (e.g., every 5 minutes)
  4. AWS CloudWatch: Stores the metrics in custom namespaces for visualization and alerting
  5. IAM Roles and Policies: Provides necessary permissions for the Step Functions workflow and scheduler

The workflow is simple:

  • The scheduler triggers the Step Functions state machine at your defined interval
  • The state machine calls the Hetzner Cloud API using the stored credentials
  • Metrics are retrieved and parsed
  • Each metric is pushed to CloudWatch with appropriate dimensions (like Server ID or Load Balancer ID)

This architecture is cost-effective, with typical monthly costs of $1-2 per monitored resource when collecting metrics every 5 minutes.

Step 2 - Retrieve Your Hetzner Cloud Resource IDs

First, you need to identify which Hetzner Cloud resources you want to monitor. You'll need either a Load Balancer ID or a Server ID.

Option A: Using the Hetzner Cloud Console

  1. Log in to Hetzner Cloud Console
  2. Select your project
  3. For Load Balancers:
    • Click on "Load Balancers" in the left menu
    • Click on the Load Balancer you want to monitor
    • The ID is shown in the URL: https://console.hetzner.cloud/projects/YOUR_PROJECT/load-balancers/LOAD_BALANCER_ID
  4. For Servers:
    • Click on "Servers" in the left menu
    • Click on the Server you want to monitor
    • The ID is shown in the URL: https://console.hetzner.cloud/projects/YOUR_PROJECT/servers/SERVER_ID

Option B: Using the Hetzner Cloud CLI

If you have the hcloud CLI installed:

# List all Load Balancers
hcloud load-balancer list

# List all Servers
hcloud server list
Enter fullscreen mode Exit fullscreen mode

Note down the ID(s) of the resources you want to monitor. You'll need these values in the next steps.

Step 3 - Set Up the Terraform Configuration

Now we'll create the Terraform configuration to deploy the monitoring infrastructure.

Create a new directory for your Terraform project:

mkdir hetzner-cloudwatch-integration
cd hetzner-cloudwatch-integration
Enter fullscreen mode Exit fullscreen mode

Create a file named main.tf with the following content for monitoring a Load Balancer:

terraform {
  required_version = ">= 1.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.0"
    }
  }
}
provider "aws" {
  region = "us-east-1"  # Change to your preferred AWS region
}
module "loadbalancer_metrics" {
  source  = "kamranbiglari/hetzner-cloudwatch-integration/aws"
  version = "~> 1.0"
  hetzner_cloud_api_token = var.hetzner_cloud_api_token
  metric_type             = "loadbalancer"
  name                    = "hetzner-lb-metrics"
  # Enable automatic metric collection every 5 minutes
  create_scheduler    = true
  schedule_expression = "rate(5 minutes)"
  data = {
    loadbalancer_id = var.loadbalancer_id
  }
}
Enter fullscreen mode Exit fullscreen mode

For monitoring a Server instead, use metric_type = "server" and provide server_id:

module "server_metrics" {
  source  = "kamranbiglari/hetzner-cloudwatch-integration/aws"
  version = "~> 1.0"
  hetzner_cloud_api_token = var.hetzner_cloud_api_token
  metric_type             = "server"
  name                    = "hetzner-server-metrics"
  create_scheduler    = true
  schedule_expression = "rate(5 minutes)"
  data = {
    server_id = var.server_id
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 4 - Define Variables

Create a variables.tf file to define the required variables:

variable "hetzner_cloud_api_token" {
  description = "Hetzner Cloud API token with read permissions"
  type        = string
  sensitive   = true
}
variable "loadbalancer_id" {
  description = "ID of the Hetzner Cloud Load Balancer to monitor"
  type        = string
}
# Or for servers:
# variable "server_id" {
#   description = "ID of the Hetzner Cloud Server to monitor"
#   type        = string
# }
Enter fullscreen mode Exit fullscreen mode

Create a terraform.tfvars file with your actual values (never commit this file to version control):

hetzner_cloud_api_token = "your-hetzner-api-token-here"
loadbalancer_id         = "123456"  # Replace with your actual ID
Enter fullscreen mode Exit fullscreen mode

Alternatively, you can set these values as environment variables:

export TF_VAR_hetzner_cloud_api_token="your-hetzner-api-token-here"
export TF_VAR_loadbalancer_id="123456"
Enter fullscreen mode Exit fullscreen mode

Step 5 - Deploy the Infrastructure

Initialize Terraform to download the required providers and modules:

terraform init
Enter fullscreen mode Exit fullscreen mode

You should see output indicating that Terraform has successfully initialized and downloaded the module.

Review the planned changes:

terraform plan
Enter fullscreen mode Exit fullscreen mode

Terraform will show you all the resources that will be created:

  • AWS EventBridge Connection (for storing the API token)
  • IAM Role and Policy for Step Functions
  • IAM Role and Policy for EventBridge Scheduler
  • Step Functions State Machine
  • EventBridge Scheduler

If everything looks correct, apply the configuration:

terraform apply
Enter fullscreen mode Exit fullscreen mode

Type yes when prompted to confirm. The deployment typically takes 30-60 seconds.

Step 6 - Verify the Deployment

After Terraform completes, let's verify that everything is working correctly.

Check the Step Functions State Machine

  1. Open the AWS Step Functions Console
  2. Select your AWS region (the one you specified in the provider block)
  3. You should see a state machine named hetzner-lb-metrics (or whatever name you specified)
  4. Wait a few minutes for the first scheduled execution to run
  5. Click on the state machine to see the execution history
  6. Click on a completed execution to see the workflow details and verify it succeeded

Check CloudWatch Metrics

  1. Open the AWS CloudWatch Console
  2. In the left navigation, click on "All metrics"
  3. You should see a custom namespace named either:
    • HetznerLoadBalancer (for Load Balancer metrics)
    • HetznerServer (for Server metrics)
  4. Click on the namespace to explore the available metrics
  5. Select a metric to view its data

For Load Balancers, you'll see metrics like:

  • open_connections: Number of currently open connections
  • connections_per_second: Rate of new connections
  • requests_per_second: HTTP/HTTPS requests processed per second
  • bandwidth_in and bandwidth_out: Network traffic in bytes per second

For Servers, you'll see metrics like:

  • cpu: CPU utilization percentage
  • disk_iops_read and disk_iops_write: Disk operations per second
  • disk_bandwidth_read and disk_bandwidth_write: Disk throughput
  • network_bandwidth_in and network_bandwidth_out: Network traffic
  • network_packets_in and network_packets_out: Packet counts

Step 7 - Create CloudWatch Dashboards (Optional)

Now that your metrics are flowing into CloudWatch, you can create custom dashboards for visualization.

  1. In the CloudWatch Console, click "Dashboards" in the left navigation
  2. Click "Create dashboard"
  3. Give it a name like "Hetzner Load Balancer Monitoring"
  4. Click "Add widget"
  5. Select "Line" or "Number" widget type
  6. Choose your metrics from the HetznerLoadBalancer or HetznerServer namespace
  7. Configure the graph settings and save the widget
  8. Add more widgets as needed to create a comprehensive dashboard

You can create graphs showing:

  • CPU usage over time for servers
  • Request rate trends for load balancers
  • Network bandwidth utilization
  • Connection counts and patterns

Step 8 - Set Up CloudWatch Alarms (Optional)

CloudWatch alarms can notify you when metrics exceed thresholds.

Here's an example of creating an alarm using Terraform. Add this to your main.tf:

resource "aws_cloudwatch_metric_alarm" "high_cpu" {
  alarm_name          = "hetzner-server-high-cpu"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 2
  metric_name         = "cpu"
  namespace           = "HetznerServer"
  period              = 300
  statistic           = "Average"
  threshold           = 80
  alarm_description   = "This metric monitors server CPU utilization"
  dimensions = {
    ServerId = var.server_id
  }
  # Optional: Add SNS topic for notifications
  # alarm_actions = [aws_sns_topic.alerts.arn]
}
Enter fullscreen mode Exit fullscreen mode

Run terraform apply again to create the alarm.

Step 9 - Monitor Multiple Resources

To monitor multiple Load Balancers or Servers, you can create multiple module instances:

# First Load Balancer
module "loadbalancer_metrics_1" {
  source  = "kamranbiglari/hetzner-cloudwatch-integration/aws"
  version = "~> 1.0"
  hetzner_cloud_api_token = var.hetzner_cloud_api_token
  metric_type             = "loadbalancer"
  name                    = "hetzner-lb-metrics-1"
  create_scheduler    = true
  schedule_expression = "rate(5 minutes)"
  data = {
    loadbalancer_id = "123456"
  }
}
# Second Load Balancer
module "loadbalancer_metrics_2" {
  source  = "kamranbiglari/hetzner-cloudwatch-integration/aws"
  version = "~> 1.0"
  # Reuse the EventBridge Connection from the first module
  create_event_connection = false
  event_connection_arn    = module.loadbalancer_metrics_1.event_connection_arn
  metric_type = "loadbalancer"
  name        = "hetzner-lb-metrics-2"
  create_scheduler    = true
  schedule_expression = "rate(5 minutes)"
  data = {
    loadbalancer_id = "789012"
  }
}
Enter fullscreen mode Exit fullscreen mode

Note that the second module reuses the EventBridge Connection from the first module to avoid storing the API token multiple times. This is more efficient and secure.

Step 10 - Query Metrics Using AWS CLI (Optional)

You can also query metrics programmatically using the AWS CLI:

# Get CPU metrics for a server
aws cloudwatch get-metric-statistics \
  --namespace HetznerServer \
  --metric-name cpu \
  --dimensions Name=ServerId,Value=789012 \
  --start-time 2025-01-01T00:00:00Z \
  --end-time 2025-01-01T01:00:00Z \
  --period 300 \
  --statistics Average

# Get requests per second for a load balancer
aws cloudwatch get-metric-statistics \
  --namespace HetznerLoadBalancer \
  --metric-name requests_per_second \
  --dimensions Name=LoadBalancerId,Value=123456 \
  --start-time 2025-01-01T00:00:00Z \
  --end-time 2025-01-01T01:00:00Z \
  --period 300 \
  --statistics Average,Maximum
Enter fullscreen mode Exit fullscreen mode

Step 11 - Customize Collection Frequency

The default collection frequency is every 5 minutes. You can adjust this by changing the schedule_expression:

# Every minute
schedule_expression = "rate(1 minute)"
# Every 10 minutes
schedule_expression = "rate(10 minutes)"
# Using cron syntax - every 5 minutes
schedule_expression = "cron(0/5 * * * ? *)"
# Every hour at minute 0
schedule_expression = "cron(0 * * * ? *)"
Enter fullscreen mode Exit fullscreen mode

Keep in mind that more frequent collections will increase your AWS costs slightly, but the impact is minimal (a few cents per month per resource).

Step 12 - Clean Up (Optional)

If you want to remove the monitoring infrastructure, run:

terraform destroy
Enter fullscreen mode Exit fullscreen mode

Type yes when prompted. This will remove all created AWS resources. Note that CloudWatch metric data is retained for 15 months by default, even after you delete the infrastructure.

Conclusion

Congratulations! You have successfully integrated your Hetzner Cloud infrastructure with AWS CloudWatch. Your metrics are now being collected automatically every 5 minutes and are available for visualization, alerting, and analysis in CloudWatch.

This integration provides several benefits:

  • Centralized monitoring: All your infrastructure metrics in one place
  • Automated collection: No manual intervention required
  • Cost-effective: Serverless architecture means you only pay for what you use
  • Scalable: Easy to add more resources to monitor
  • Secure: API tokens are stored encrypted in AWS Secrets Manager

Next steps you might consider:

  • Create comprehensive CloudWatch dashboards for your team
  • Set up CloudWatch alarms with SNS notifications for critical metrics
  • Integrate with AWS CloudWatch Insights for advanced querying
  • Export metrics to other tools using CloudWatch Metric Streams
  • Explore the Terraform module documentation for advanced configuration options

Top comments (0)