⚠️ Experimental (Beta) — This is an unofficial community implementation, under active development and not production-ready. APIs, variables, and behavior may change without notice. Use at your own risk. No stability guarantees are provided until v1.0.0.
An OpenTofu/Terraform module that deploys a Rancher management cluster on Hetzner Cloud with built-in Hetzner Node Driver for downstream cluster provisioning via the Rancher UI.
This module is part of the RKE2-on-Hetzner ecosystem — a set of interconnected projects that together provide a complete Kubernetes management platform on Hetzner Cloud.
| Repository | Role in Ecosystem |
|---|---|
terraform-hcloud-rke2-core |
L3 infrastructure primitive — servers, network, readiness |
terraform-hcloud-rancher (this repo) |
Management cluster — Rancher + Node Driver on RKE2 |
rancher-hetzner-cluster-templates |
Downstream cluster provisioning via Rancher UI |
rancher-hcloud-image-controller |
OSS image controller — watches HetznerConfig, resolves golden:*, triggers build Jobs (ADR-012) |
Golden-image build backend: pending selection. The previous Packer-based image builder was dropped (non-free license + poor Hetzner Cloud compatibility); an open-source replacement has not yet been selected. The golden-image delivery concept continues via the OSS image controller (ADR-012).
rke2-core (L3 infra) → rancher (L3+L4 management) → cluster-templates (downstream via UI)
↑
image controller (golden:* → build backend pending)
- Single
tofu apply— provisions RKE2 cluster, installs cert-manager, Rancher, bootstraps admin, installs Hetzner Node Driver - True Zero-SSH — no SSH keys, no port 22, no sshd (inherited from
terraform-hcloud-rke2-core, ADR-002) - sslip.io by default — auto-generates hostname from ingress LB IP, zero DNS setup needed
- BYO DNS — pass
rancher_hostnamewith any FQDN (Route53, Cloudflare, etc.) for production - BYO Ingress LB — create or bring your own load balancer (
for_eachgating pattern) - Hetzner Node Driver — zsys-studio/rancher-hetzner-cluster-provider installed via cloud-init
- TLS flexibility — self-signed (Rancher CA), Let's Encrypt, or user-provided certificate
- Dual Load Balancer — control-plane LB (K8s API) + ingress LB (Rancher UI)
- CIS hardening — optional RKE2 CIS profile via
enable_ciswith automatic PSA exemptions for Rancher - 2 providers only —
hcloud+rancher2(all L4 via cloud-init manifests)
| Tool | Version | Purpose |
|---|---|---|
| OpenTofu | >= 1.8.0 | Infrastructure as Code |
| Hetzner Cloud | API token | Cloud provider |
module "rancher" {
source = "git::https://github.com/mbilan1/terraform-hcloud-rancher.git?ref=v0.1.0"
hcloud_api_token = var.hcloud_api_token
admin_password = var.admin_password
# rancher_hostname is optional — defaults to sslip.io auto-hostname
# For production, set a real FQDN:
# rancher_hostname = "rancher.example.com"
}See examples/minimal/ for a complete working example.
By default, the module auto-generates a sslip.io hostname from the ingress LB IP — no DNS setup is required.
For production, bring your own DNS (Route53, Cloudflare, or any provider):
- Deploy with default sslip.io to obtain
ingress_lb_ipv4output - Create an A record in your DNS provider:
rancher.example.com → <ingress_lb_ipv4> - Re-apply with the FQDN:
module "rancher" {
source = "git::https://github.com/mbilan1/terraform-hcloud-rancher.git?ref=v0.1.0"
hcloud_api_token = var.hcloud_api_token
rancher_hostname = "rancher.example.com" # Your R53/Cloudflare managed FQDN
admin_password = var.admin_password
tls_source = "letsEncrypt"
letsencrypt_email = "ops@example.com"
}Route53 example: Create an
aws_route53_recordalongside this module, pointing at theingress_lb_ipv4output. See the architecture docs for details.
See docs/ARCHITECTURE.md for the full design document including:
- Module architecture (L3 infrastructure + L4 Kubernetes management)
- Infrastructure topology (dual LB design)
- Deployment flow (two-phase apply)
- Downstream cluster provisioning via Rancher UI
- Security model
- Compromise log
terraform-hcloud-rancher/
├── main.tf # Root facade — BYO LB + cloud-init manifests + module calls
├── variables.tf # All user-facing input variables
├── outputs.tf # Module outputs
├── providers.tf # Provider configurations (hcloud + rancher2)
├── versions.tf # Provider version constraints
├── guardrails.tf # Preflight check {} blocks
├── moved.tf # State migration (singleton → for_each)
├── modules/
│ ├── rke2-cluster/ # L3: Hetzner infrastructure via terraform-hcloud-rke2-core
│ └── rancher/ # L4: Rancher admin bootstrap only (rancher2_bootstrap)
├── examples/
│ ├── minimal/ # Minimal working deployment (sslip.io, self-signed TLS)
│ └── complete/ # HA 3-node with BYO firewall, Let's Encrypt, Packer image
├── tests/ # OpenTofu unit tests (tofu test)
└── docs/
└── ARCHITECTURE.md # Full design documentation
| Provider | Source | Version | Purpose |
|---|---|---|---|
| hcloud | hetznercloud/hcloud | 1.60.1 | Hetzner Cloud resources |
| rancher2 | rancher/rancher2 | 13.1.4 | Rancher bootstrap |
Note: cert-manager, Rancher, NodeDriver, and UIPlugin are deployed via RKE2 cloud-init manifests (HelmChart CRDs). No
helm,kubernetes, orkubectlproviders needed.
| Name | Version |
|---|---|
| terraform | >= 1.8.0 |
| hcloud | = 1.60.1 |
| rancher2 | = 13.1.4 |
| random | = 3.8.1 |
| Name | Version |
|---|---|
| hcloud | 1.60.1 |
| random | 3.8.1 |
| Name | Type |
|---|---|
| hcloud_load_balancer.ingress | resource |
| hcloud_load_balancer_network.ingress | resource |
| hcloud_load_balancer_service.http | resource |
| hcloud_load_balancer_service.https | resource |
| hcloud_load_balancer_target.ingress | resource |
| random_password.admin | resource |
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| hcloud_api_token | Hetzner Cloud API token for the management project (read/write access required) | string |
n/a | yes |
| admin_password | Initial password for the Rancher 'admin' user. Leave empty to auto-generate a secure random password (output as rancher_admin_password). | string |
"" |
no |
| cert_manager_version | cert-manager Helm chart version to install. | string |
"1.17.2" |
no |
| cluster_name | Identifier prefix for all provisioned resources (servers, LBs, network). Must be lowercase alphanumeric, max 20 characters. | string |
"rancher" |
no |
| control_plane_server_type | Hetzner Cloud server type for management cluster nodes. Minimum cx43 (8 vCPU, 16 GB) for Rancher. | string |
"cx43" |
no |
| create_ingress_lb | Create a Hetzner ingress load balancer for Rancher UI (ports 80/443). Set to false when using a pre-existing or external load balancer. | bool |
true |
no |
| enable_cis | Enable CIS hardening for the management cluster. Activates RKE2 CIS profile, creates prerequisites (etcd user, kernel params), and exempts cattle-system from PodSecurity restricted policy. Works with both stock ubuntu-24.04 and Packer golden images. | bool |
false |
no |
| existing_ingress_lb_ipv4 | IPv4 address of an existing ingress load balancer. Only used when create_ingress_lb = false. If set, auto-generates hostname from this IP (unless rancher_hostname is provided). | string |
"" |
no |
| firewall_ids | List of Hetzner firewall IDs to attach to all management cluster nodes. BYO: create firewalls externally and pass their IDs. | list(number) |
[] |
no |
| hcloud_image | OS image for management cluster nodes. Use 'ubuntu-24.04' (default) or a Hetzner snapshot ID from a Packer baked image. | string |
"ubuntu-24.04" |
no |
| hcloud_network_cidr | IPv4 address range for the Hetzner private network in CIDR notation. | string |
"10.0.0.0/16" |
no |
| hcloud_network_zone | Hetzner network zone encompassing all node locations. | string |
"eu-central" |
no |
| hetzner_driver_version | Version of zsys-studio/rancher-hetzner-cluster-provider to install as Rancher Node Driver. | string |
"0.9.0" |
no |
| install_hetzner_driver | Install zsys-studio Hetzner Node Driver for downstream cluster provisioning via Rancher UI. Set to false if the driver is managed externally. | bool |
true |
no |
| letsencrypt_email | Email address for Let's Encrypt certificate registration. Required when tls_source = 'letsEncrypt', ignored otherwise. | string |
"" |
no |
| management_node_count | Number of control-plane nodes for the management cluster. Use 1 for dev/test, 3 for HA production. | number |
1 |
no |
| node_location | Primary Hetzner datacenter location for management cluster nodes (e.g. 'hel1', 'nbg1', 'fsn1'). | string |
"hel1" |
no |
| rancher_hostname | FQDN for the Rancher UI (e.g. 'rancher.example.com'). Leave empty to auto-generate from ingress LB IP via sslip.io. | string |
"" |
no |
| rancher_version | Rancher Helm chart version to install (e.g. '2.13.3'). Must be compatible with the Kubernetes version. | string |
"2.13.3" |
no |
| rke2_config | Additional RKE2 config.yaml content appended to every management cluster node. | string |
"etcd-snapshot-schedule-cron: \"0 */6 * * *\"\netcd-snapshot-retention: 10\n" |
no |
| rke2_version | RKE2 release tag to deploy (e.g. 'v1.34.4+rke2r1'). Leave empty for stable channel. | string |
"v1.34.4+rke2r1" |
no |
| ssh_key_ids | List of Hetzner SSH key IDs to install on management cluster nodes. Empty by default (Zero-SSH). | list(number) |
[] |
no |
| subnet_address | Subnet allocation for cluster nodes within the private network. | string |
"10.0.1.0/24" |
no |
| tls_source | TLS certificate source for Rancher. 'rancher' = self-signed CA generated by Rancher, 'letsEncrypt' = Let's Encrypt via cert-manager ACME, 'secret' = user-provided TLS secret. | string |
"rancher" |
no |
| Name | Description |
|---|---|
| ingress_lb_ipv4 | IPv4 address of the ingress load balancer (Rancher UI). Point DNS A record here. |
| initial_master_ipv4 | Public IPv4 of the initial master (control-plane node that bootstrapped the cluster) |
| network_id | Hetzner Cloud private network ID (for cluster template pre-fill — ADR-005) |
| rancher_admin_password | Rancher admin password (auto-generated if not provided). Use to log in at rancher_url. |
| rancher_admin_token | Rancher admin API token for initial configuration. Treat as a secret. |
| rancher_hostname | Effective Rancher hostname (auto-generated from LB IP if not provided) |
| rancher_url | Rancher UI URL (HTTPS) |
MIT — Copyright (c) 2026 Maksym Bilan