Hetzner from an Automation Perspective

Hetzner from an Automation Perspective

Hetzner Cloud is an EU-based relatively cheap cloud computing platform, at least when comparing to something like AWS. They offer a simple set of services required in everyday small-scale projects: servers, volumes, simple load balancing features and an S3-compatible object storage. But how does it fare from an automation point of view?

Terraform support for Hetzner

We’ve been using Hetzner Cloud both internally and with customer projects for spinning up good old virtual servers for stable, predictable workloads and web services. The amount of services we have running there has been growing steadily, so it was high time to look into the automation options to get more standardized stacks and ease the maintenance workload.

Luckily, Hetzner has a terraform provider that can be used to configure most things with relative ease. For example, here’s a snapshot of how you would configure a simple server with Hetzner terraform:

resource "hcloud_server" "web" {
  name        = "test-service"
  image       = "docker-ce"
  server_type = "cpx11"
  public_net {
    ipv4_enabled = true
    ipv6_enabled = true
  }
  ssh_keys     = data.hcloud_ssh_keys.by_label.ssh_keys[*].id
  datacenter   = "hel1-dc2" # Helsinki, Finland
  firewall_ids = [hcloud_firewall.test-service-firewall.id]
}

Seems easy. Notice that Hetzner offers a nice pre-configured image (or “App”) with docker and docker compose installed on a Ubuntu 24.04 base OS. This suits our needs nicely, since we use these tools to run many of our services. Access to the server is configured by SSH keys, which you can control either by labels or by specifying the actual public keys directly in your repository.

However, in our day-to-day operations, there have been multiple “gotchas” compared to AWS that came as a surprise and needed to be worked around. Here are some of them to give you an idea.

Gotcha 1: Server access and permission management

TL;DR: Prepare to write a lot of extra automation scripts

Since Hetzner does not have a similar IAM or SSM system as AWS, access to servers is handled entirely with SSH keys that cannot be modified afterwards with Terraform without recreating the whole server. You need some other custom automation (Ansible, PyInfra) to manage access more automatically and also make sure to add the following to not accidentally destroy your servers if you modify labels:

  lifecycle {
    ignore_changes = [
      ssh_keys
    ]
  }

Permissions are also handled by normal linux systems (chmod, groups). These can be handled initially by userdata cloudinit scripts, but as with AWS, userdata executes only during the server creation. AWS at least allows other connectivity options and automation tools around this. With Hetzner, when you want to add, remove or modify the users who have access to your servers, you need external tooling. Or maybe don’t give anyone access to the servers at all?

Gotcha 2: Permanent floating IP assignment

TL;DR: More scripts to actually apply the assignment

Floating IPs are a useful tool to allow, for example, more dynamic service reassignments to new servers in maintenance situations. Here’s how you configure a floating (elastic) IP with AWS Terraform:

resource "aws_eip" "app" {
  vpc = true
  tags = { Name = "app-1-eip" }
}

resource "aws_eip_association" "app" {
  allocation_id = aws_eip.app.id
  instance_id   = aws_instance.app.id
}

Here’s how you do it with Hetzner:

resource "hcloud_floating_ip" "app_ipv4" {
  name          = "app-ipv4"
  type          = "ipv4"
  home_location = "hel1"
}

resource "hcloud_floating_ip_assignment" "app_ipv4" {
  floating_ip_id = hcloud_floating_ip.app_ipv4.id
  server_id      = hcloud_server.web.id
}

Pretty simple, eh? Except it’s not: with Hetzner you need to have some extra config files and run some commands directly on the server to apply the config and make it persistent. In practice, we have the following cloudinit config template that is populated by terraform and then executed with netplan apply:

write_files:
  - path: /etc/netplan/99-floating-ip.yaml
    permissions: "0644"
    content: |
      network:
        version: 2
        ethernets:
          eth0:
            dhcp4: true
            addresses:
              - ${floating_ip}/32

This one was a real “You’re absolutely right! It does not work like that.” -moment. And don’t even get me started on ipv6!

Gotcha 3: API token management and permissions

TL;DR: Manual token creation with binary permissions

When automating AWS infrastructure, you can create IAM users and their access credentials directly with Terraform.

With Hetzner, API tokens must be created manually through the web console. Then you copy them into your CI/CD secrets, environment variables, or wherever needed. You can’t fully bootstrap infrastructure from scratch without manual console access as the first step before any automation can begin.

Hetzner tokens also only have two permission levels: read-only or read+write. That’s it. You can’t create a token that can only manage servers but not delete firewalls. You can’t create a token that expires after an hour. You can’t restrict access by IP address or resource tags. A read+write token has full access to everything in your project. So prepare to create a lot of projects manually, too (default limit of 20 per account).

This means your Terraform CI/CD pipeline, which needs write access to create resources, has the exact same permissions as your admin console access. With AWS, you’d scope that CI/CD role to only what it needs and use temporary credentials that expire automatically.

Is Hetzner still worth it?

Despite these automation gotchas, we continue to use Hetzner Cloud for many of our projects. For small to medium-sized workloads with predictable requirements, Hetzner’s simplicity and cost-effectiveness often outweigh the automation limitations. You just need to accept that you’ll be writing some additional scripts and maintaining a bit more tooling around the edges.

The Terraform provider covers the core infrastructure resources well enough, and tools like Ansible or PyInfra can fill in the gaps for server configuration. Once you have your automation scripts in place, the day-to-day operations run smoothly. The initial setup requires the most mental adjustment when comparing to AWS.

If you’re considering Hetzner for your infrastructure or need help automating your existing Hetzner setup, feel free to contact us at info@interjektio.fi. We have plenty of knowledge on how to spin up and maintain stuff consistently on Hetzner Cloud.

Just drop us a line by filling in the form below or send a mail to info@interjektio.fi

We will contact you shortly :)