You can give the Kubernetes API server a domain name instead of accessing the cluster through a raw IP address. For example: ```text k8s.example.com ``` Syself Autopilot uses that domain as the stable control-plane endpoint, places it in the API server TLS certificate, and configures the control-plane load balancer to serve it. Application domains are separate from the cluster API endpoint. You can later expose workloads under domains such as `app.example.com` using Kubernetes Services and ingress or gateway resources. ## Before you create the cluster The API server domain is configured when the cluster is created. You need: - A domain you control. - A Hetzner Cloud Load Balancer in the same Hetzner project. - A DNS record pointing the domain to that load balancer. > [!WARNING] > The API server domain cannot be changed after the cluster has been provisioned. > > Choose the endpoint before creating the cluster. ## Step 1: Create the control-plane load balancer Create a Load Balancer in the Hetzner Cloud Console and give it a recognizable name, for example: ```text mycluster-controlplane ``` Remove the default service created by Hetzner and note the assigned public IPv4 address. Syself Autopilot does not create or delete this load balancer. It manages the control-plane targets attached to it. This means deleting the Kubernetes cluster does not delete the load balancer from your Hetzner project. ### Choose a load balancer type Hetzner provides several load balancer sizes: | Type | Max concurrent connections | Services | Targets | Certificates | | ------ | -------------------------: | -------: | ------: | -----------: | | `lb11` | 10,000 | 5 | 25 | 10 | | `lb21` | 20,000 | 15 | 75 | 25 | | `lb31` | 40,000 | 30 | 150 | 50 | For most clusters, `lb11` is sufficient. Choose a larger type when the cluster requires more load balancer capacity. ## Step 2: Create the DNS record Create an `A` record for the cluster endpoint and point it to the load balancer IPv4 address. For example: ```text k8s.example.com → 203.0.113.10 ``` The API endpoint uses IPv4 internally, so an `A` record is sufficient for the control-plane domain. DNS changes may take time to propagate depending on your DNS provider and record TTL. Check the result with: ```console $ dig k8s.example.com ``` The returned address should match the IPv4 address of the Hetzner Load Balancer. ## Step 3: Configure the Cluster Set the endpoint and load balancer variables in `spec.topology.variables`: ```yaml vars persistBlur title="cluster.yaml" apiVersion: cluster.x-k8s.io/v1beta2 kind: Cluster metadata: name: mycluster namespace: my-organization spec: topology: classRef: name: hetzner-apalla-1-36-v1 version: v1.36.3 controlPlane: class: hcloud replicas: 3 workers: machineDeployments: - class: workeramd64hcloud name: general replicas: 3 failureDomain: nbg1 variables: overrides: - name: workerMachineTypeHcloud value: cpx41 variables: - name: region value: nbg1 - name: controlPlaneMachineTypeHcloud value: cpx41 - name: clusterEndpointHost value: "k8s.example.com" - name: clusterLoadBalancerType value: "lb11" - name: clusterLoadBalancerName value: "mycluster-controlplane" ``` `clusterEndpointHost` defines the hostname clients use to reach the Kubernetes API server. `clusterLoadBalancerName` tells Syself Autopilot which existing Hetzner Load Balancer should receive the control-plane nodes as targets, while `clusterLoadBalancerType` describes its type. Apply the Cluster to the management cluster as usual: ```console $ kubectl apply -f cluster.yaml ``` See [Create a cluster](/docs/hetzner/apalla/clusters/configure/create-a-cluster) for the complete cluster creation flow. ## Why the API domain is fixed The cluster endpoint is more than a DNS convenience. During cluster bootstrap, Syself Autopilot uses `clusterEndpointHost` as part of the control-plane identity and connectivity configuration. The hostname is also included in the API server TLS certificate so clients can verify that they are connecting to the correct Kubernetes API server. In simplified form: ```mermaid flowchart LR D["k8s.example.com"]:::external --> LB["Hetzner Load Balancer"]:::external LB --> CP1["control-plane-1"]:::platform LB --> CP2["control-plane-2"]:::platform LB --> CP3["control-plane-3"]:::platform ``` Changing the hostname later would require changing the endpoint used throughout the cluster and replacing the associated API server certificate configuration. Syself Autopilot therefore treats the API domain as a creation-time setting rather than an ordinary mutable cluster variable. ## Verify the endpoint After the cluster is ready, inspect its kubeconfig: ```console $ kubectl get secret mycluster-kubeconfig \ -n my-organization \ -o jsonpath='{.data.value}' \ | base64 -d ``` The server field should use your configured hostname: ```yaml clusters: - cluster: server: https://k8s.example.com:443 ``` Test access: ```console $ KUBECONFIG=./mycluster-kubeconfig.yaml kubectl get nodes ``` If the command succeeds and the nodes report `Ready`, DNS, TLS, the load balancer, and the Kubernetes API endpoint are working together. ## Application domains are separate The cluster API domain is only for Kubernetes management traffic. Applications normally use their own domains: ```text k8s.example.com Kubernetes API app.example.com Application grafana.example.com Monitoring api.example.com Public API ``` When you expose an application through a Kubernetes `Service` of type `LoadBalancer`, the Hetzner cloud-controller-manager can provision a separate application load balancer. Hetzner application load balancers can expose both public IPv4 and IPv6 addresses. You can therefore create both DNS records when appropriate: ```text A app.example.com → AAAA app.example.com → ``` The load balancer terminates the public edge connectivity while the cluster itself continues using IPv4 internally. How HTTP or HTTPS traffic reaches the application after that depends on whether you use a direct Service, an ingress controller, or a gateway. See [Expose an application](/docs/hetzner/apalla/network/expose/choose-how-to-expose). ## API access and authentication Giving the API server a domain does not change Kubernetes authentication. The endpoint still requires valid Kubernetes credentials. For team access, configure OIDC so users authenticate through your organization's identity provider instead of distributing static administrative kubeconfigs. See [Configure OIDC](/docs/hetzner/apalla/clusters/configure/configure-oidc). ## Related - [Networking](/docs/hetzner/apalla/concepts/internals/networking) - [Create a cluster](/docs/hetzner/apalla/clusters/configure/create-a-cluster) - [Configure OIDC](/docs/hetzner/apalla/clusters/configure/configure-oidc) - [Expose an application](/docs/hetzner/apalla/network/expose/choose-how-to-expose)