terraform-hcloud-kube-hetzner

repository·master·Indexed 26 days ago

https://github.com/mysticaltech/terraform-hcloud-kube-hetzner

A Terraform module for deploying production-ready, auto-upgradable Kubernetes clusters on Hetzner Cloud using k3s (default) or RKE2. It supports high availability, autoscaling, private networking, and advanced CNI configurations including Cilium Gateway API and experimental multinetwork public overlay modes. The module also provides integration patterns for ArgoCD via Helm, Cloudflare Tunnel for API access, and Tailscale for node transport.

Tokens
65.6K
Snippets
137
Records
313
Agent score
86%

What's inside terraform-hcloud-kube-hetzner

  1. Connect to the cluster via Kube API

    master

    To access your cluster, retrieve the kubeconfig via Terraform outputs.

    To view the kubeconfig:

    terraform output kubeconfig

    To use it with kubectl:

    # Set as default
    export KUBECONFIG=/<path-to>/clustername_kubeconfig.yaml
    
    # Or use directly
    kubectl --kubeconfig clustername_kubeconfig.yaml get nodes

    If create_kubeconfig = false was set in your configuration, generate it manually using:

    terraform output --raw kubeconfig > clustername_kubeconfig.yaml
    terraform output kubeconfig
    terraform output -json kubeconfig | jq
  2. Enable Encryption at Rest for HCloud CSI

    master

    To use encrypted HCloud volumes, create a Kubernetes secret containing the passphrase and then define a StorageClass that references this secret via the csi.storage.k8s.io/node-publish-secret-name parameter.

    # 1. Create the secret
    apiVersion: v1
    kind: Secret
    metadata:
      name: encryption-secret
      namespace: kube-system
    stringData:
      encryption-passphrase: foobar
    ---
    # 2. Create the storage class
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: hcloud-volumes-encrypted
    provisioner: csi.hetzner.cloud
    reclaimPolicy: Delete
    volumeBindingMode: WaitForFirstConsumer
    allowVolumeExpansion: true
    parameters:
      csi.storage.k8s.io/node-publish-secret-name: encryption-secret
      csi.storage.k8s.io/node-publish-secret-namespace: kube-system
  3. Validate release or topology changes

    master

    Use the following command sequence to validate the Terraform/OpenTofu configuration and custom validation scripts before applying changes:

    terraform fmt -recursive
    terraform-docs markdown . > docs/terraform.md
    terraform init -backend=false
    terraform validate
    tofu init -backend=false
    tofu validate
    uv run scripts/validate_tailscale_large_scale_examples.py
    uv run scripts/validate_v3_final_polish_examples.py
    git diff --check
  4. Select a v3 cluster topology

    master

    Choose a topology based on your requirements:

    NeedRecommended v3 topology
    Small dev clusterSingle control plane, one agent pool, no ingress unless needed.
    Normal HA3 control planes, 2+ agents, one primary Hetzner Network, public API LB restricted to source CIDRs or secure endpoint.
    Private-onlynat_router plus private control-plane LB on the primary Network.
    Secure operator/API accessnode_transport_mode = "tailscale" with public API/SSH firewall sources closed.
    Cilium Gateway APICilium, enable_kube_proxy = false, cilium_gateway_api_enabled = true.
    Heavy image-pull pressureembedded_registry_mirror.enabled = true on trusted clusters.
  5. Use Cilium Multinetwork Preview Pattern for v3

    master

    This pattern allows Kubernetes clusters to span multiple Hetzner Cloud Networks using the experimental cilium_public_overlay mode. In this mode, Cilium uses public node addresses for tunnel or WireGuard peer reachability, while Hetzner private Networks remain separate attachment domains.

    Important Considerations:

    • This is an experimental v3 preview and is not production-supported until live cross-network Cilium datapath tests pass.
    • For production private multinetwork scale, use node_transport_mode = "tailscale" instead.
    • This mode is specifically for Hetzner Cloud nodes; it does not support Robot/vSwitch exposure.
    • Control planes remain on the primary kube-hetzner network.
    • You must provide a public join path: either via a module-managed public control-plane Load Balancer or by setting an explicit control_plane_endpoint.

    Validation: Always run terraform plan or tofu plan before applying, as this mode is designed to fail early if the topology is invalid.

    cni_plugin = "cilium"
    
    enable_experimental_cilium_public_overlay = true
    multinetwork_mode                         = "cilium_public_overlay"
    multinetwork_transport_ip_family          = "ipv4" # ipv4 | ipv6 | dualstack
    
    enable_control_plane_load_balancer                   = true
    control_plane_load_balancer_enable_public_network    = true
    load_balancer_enable_public_network                  = true
    multinetwork_cilium_peer_ipv4_cidrs                  = ["0.0.0.0/0"]
    
    agent_nodepools = [
      {
        name        = "agents-primary"
        server_type = "cx23"
        location    = "nbg1"
        count       = 50
      },
      {
        name        = "agents-secondary"
        server_type = "cx23"
        location    = "nbg1"
        count       = 50
        network_id  = 11959154
      },
    ]
    
    autoscaler_nodepools = [
      {
        name        = "autoscaled-primary"
        server_type = "cx23"
        location    = "nbg1"
        min_nodes   = 0
        max_nodes   = 25
      },
      {
        name        = "autoscaled-secondary"
        server_type = "cx23"
        location    = "nbg1"
        min_nodes   = 0
        max_nodes   = 25
        network_id  = 11959154
      },
    ]
  6. Deploy a Kubernetes cluster with Terraform or OpenTofu

    master

    Once your project folder is initialized and kube.tf is customized, use Terraform or OpenTofu to deploy the infrastructure.

    Warning: Once Terraform manages your cluster, avoid making manual changes in the Hetzner Cloud UI. Use the hcloud CLI for inspection.

    ### Using Terraform
    ```sh
    cd <your-project-folder>
    terraform init --upgrade
    terraform validate
    terraform plan
    terraform apply -auto-approve

    Using OpenTofu

    tofu init --upgrade
    tofu validate
    tofu plan
    tofu apply -auto-approve
  7. Verify no protected infrastructure is destroyed during v3 upgrade

    master

    To ensure a safe in-place upgrade, use this command to check if the Terraform plan proposes destroying any critical Hetzner Cloud resources.

    Contract: If the command produces no output, the protected-infrastructure gate passed. If it produces any output, stop immediately and investigate the proposed deletions before applying.

    terraform show -json v3-upgrade.tfplan \
      | jq -r '
          .resource_changes[]?\
          | select(.type as $type | [
              "hcloud_server",
              "hcloud_network",
              "hcloud_network_subnet",
              "hcloud_load_balancer",
              "hcloud_volume",
              "hcloud_primary_ip",
              "hcloud_placement_group",
              "hcloud_firewall"
            ] | index($type))\
          | select(.change.actions | index("delete"))\
          | "\(.address): \(.type) \(.change.actions | join(","))"
        '
  8. Enable Cluster Autoscaling

    master

    Autoscaling is enabled via the autoscaler_nodepools variable and is powered by the Kubernetes Cluster Autoscaler.

    Important Considerations:

    • Snapshots: Autoscaled nodes use a snapshot from the initial control plane. Ensure disk sizes match.
    • Longhorn Storage: Keep Longhorn on static agent nodepools. Autoscaled Longhorn volumes require a write-capable Hetzner token in node user-data and can leave detached volumes behind during scale-down.
    • Local Storage: The autoscaler will not scale down nodes running pods with local storage unless configured. To allow eviction of such pods, add --skip-nodes-with-local-storage=false to cluster_autoscaler_extra_args or annotate pods with cluster-autoscaler.kubernetes.io/safe-to-evict: "true".
  9. Migrate from v2 to v3

    master

    To upgrade from v2.x to v3.x, follow the guided migration process. It is highly recommended to review MIGRATION.md and docs/v2-to-v3-migration.md before proceeding.

    Manual Migration Path

    1. Audit your Terraform root.
    2. Run the migration assistant script:
      uv run python /path/to/kube-hetzner/scripts/v2_to_v3_migration_assistant.py --root .
    3. Initialize and plan the upgrade:
      terraform init -upgrade
      terraform plan
    4. Review all planned resource actions before applying. Only apply if there are no unexpected delete, replace, or forces replacement actions for critical infrastructure.

    v3 Readiness Checklist

    • Back up current state with terraform state pull.
    • Verify removed v2 inputs are gone and renamed booleans with inverted meanings are reviewed.
    • Review addon version policy (unset variables use deterministic defaults; latest uses upstream floating behavior).
    • Ensure terraform validate passes.
    • For private/special clusters (Robot/vSwitch, Tailscale, Longhorn, etc.), ensure you have a rollback or blue/green plan.
    uv run python /path/to/kube-hetzner/scripts/v2_to_v3_migration_assistant.py --root .
    terraform init -upgrade
    terraform plan
  10. Enable scheduled etcd backups to S3

    master

    To enable automated, scheduled etcd backups to S3 in your kube.tf configuration, follow these steps:

    1. Populate the etcd_s3_backup configuration in your kube.tf file.
    2. Export the cluster_token as a Terraform output so it can be retrieved later:
    output "cluster_token" {
      value     = module.kube-hetzner.cluster_token
      sensitive = true
    }
    1. Verify you can access the token using the command terraform output cluster_token.
    output "cluster_token" {
      value     = module.kube-hetzner.cluster_token
      sensitive = true
    }
  11. Configure Nodepool Placement Groups

    master

    Hetzner Placement Groups ensure servers are located on different physical hosts to improve fault tolerance.

    • Limits: Each placement group supports a maximum of 10 servers. The project limit is 50 groups.
    • Auto-sharding: If enable_placement_groups is true and you do not specify a placement_group, the module automatically shards nodepools every 10 servers.
    • Manual Assignment: To group specific nodes together, provide the same placement_group name to multiple nodepool maps.
  12. Propose a kube-hetzner SELinux rule upstream

    master

    If you want to contribute a new rule to the kube-hetzner repository, ensure your proposal includes the following evidence:

    • The exact AVC denial lines.
    • The name and version of the workload.
    • The Kubernetes distribution used (k3s or rke2).
    • The OS image used (leapmicro or microos).
    • Confirmation of whether a targeted udica policy successfully resolved the issue.

    Rules without real AVC evidence will not be accepted into the main policy.