Skip to main content

Create a Cluster

Inspect 1.36

A workload cluster in Syself Autopilot is declared with a single Kubernetes Cluster object.

The object defines which release the cluster runs, its Kubernetes version, control plane and worker capacity, infrastructure settings, and other cluster-level configuration.

You apply the Cluster object to the Syself Autopilot management cluster. From there, Syself Autopilot creates and provisions the required servers, bootstraps Kubernetes, installs the platform components, and continuously reconciles the cluster to the state you declared.

These steps create a standard Hetzner Cloud cluster. You can extend the same Cluster object with additional worker pools, bare-metal servers, OIDC, custom load balancer settings, and other supported configuration.

Before you begin#

You need:

  • Access to your Syself management cluster with kubectl.
  • An organization namespace containing the ClusterClass for the Kubernetes version you want to use.
  • Enough Hetzner Cloud quota for the control plane and worker servers you want to create.

Follow the steps in if you do not fulfill all the requirements.

The Cluster object#

A Syself Autopilot cluster is configured through the Cluster resource.

The important parts are:

Field What it defines
metadata.name Name of the workload cluster
spec.topology.classRef Cluster Stack release and resulting ClusterClass used by the cluster
spec.topology.version Kubernetes version
spec.topology.controlPlane Control plane type and replica count
spec.topology.workers Worker pools and their replica counts
spec.topology.variables Cluster-level infrastructure and platform configuration
spec.clusterNetwork Pod and Service networks and the service domain

The Cluster Stack defines the supported platform and available configuration. The Cluster object defines the infrastructure you want and which supported settings should be used for this cluster.

Step 1: Choose a Cluster Stack release#

Syself provides the Cluster Stack releases in your management cluster and keeps them available for you to use. List the ones you can run:

		$ kubectl get clusterstackrelease
NAME                     K8S VERSION   READY   AGE   REASON   MESSAGE
hetzner-apalla-1-36-v1   v1.36.3       true    2d
	

Choose the release you want to run.

You need two values from it:

  • NAME, used by spec.topology.classRef.name.
  • K8S VERSION, used by spec.topology.version.

For the example above:

yaml
		classRef:
  name: hetzner-apalla-1-36-v1
version: v1.36.3
	

The release identifies the complete Syself platform version: Kubernetes, Syself Linux, Cluster API templates, networking, storage integrations, in-cluster components, lifecycle behavior, and the other components tested together by Syself.

See for how releases work.

Step 2: Define the cluster#

Create a file named cluster.yaml:

cluster.yamlyaml
		apiVersion: cluster.x-k8s.io/v1beta2
kind: Cluster
metadata:
  name: mycluster
  namespace: my-organization
spec:
  clusterNetwork:
    services:
      cidrBlocks: ["10.128.0.0/12"]
    pods:
      cidrBlocks: ["192.168.0.0/16"]
    serviceDomain: "cluster.local"
  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
	

This object is enough to declare the cluster.

Syself Autopilot derives the underlying control plane, MachineDeployments, Machines, infrastructure resources, bootstrap configuration, node images, and in-cluster components from the selected Cluster Stack.

Cluster metadata#

The Cluster is a normal Kubernetes object, so you can add your own labels and annotations under metadata.

For example:

yaml
		metadata:
  name: mycluster
  labels:
    environment: production
    client: example-company
	

Labels are useful for organizing clusters and integrating them with your own automation.

These labels describe the Cluster object in the management cluster. They do not automatically become labels on every node or workload inside the workload cluster.

Control plane sizing#

The control plane is configured under:

yaml
		controlPlane:
  class: hcloud
  replicas: 3
	

replicas defines how many control plane servers Syself Autopilot maintains.

A three-node control plane provides multiple API server and etcd members and allows the platform to replace control plane nodes one at a time while maintaining quorum.

The server type is configured separately through the cluster-level variable:

yaml
		- name: controlPlaneMachineTypeHcloud
  value: cpx41
	

This separates how many control plane servers you want from which type of server they should use.

Worker pools#

Workers are defined as MachineDeployments:

yaml
		workers:
  machineDeployments:
    - class: workeramd64hcloud
      name: general
      replicas: 3
      failureDomain: nbg1
	

Each MachineDeployment is one independently managed worker pool.

replicas determines how many workers Syself Autopilot maintains in that pool.

The worker machine type can be configured for the pool through a variable override:

yaml
		variables:
  overrides:
    - name: workerMachineTypeHcloud
      value: cpx41
	

This becomes useful when a cluster has multiple worker pools.

For example, you might have:

  • A general-purpose worker pool.
  • A larger compute worker pool.
  • A bare-metal database pool.
  • A GPU worker pool.

Each pool can have its own replica count and supported infrastructure settings while remaining part of the same workload cluster.

Region and machine types#

The cluster's Hetzner region is configured with:

yaml
		variables:
  - name: region
    value: nbg1
	

The example uses nbg1.

The control plane server type is configured at cluster level:

yaml
		- name: controlPlaneMachineTypeHcloud
  value: cpx41
	

Worker machine types can be set independently for each worker pool:

yaml
		variables:
  overrides:
    - name: workerMachineTypeHcloud
      value: cpx41
	

This lets you size control plane and workload capacity separately.

Pod and Service networks#

Every workload cluster needs dedicated IP ranges for pods and Kubernetes Services.

spec.clusterNetwork is part of the cluster's required configuration, so every Cluster object carries it. The values below are the ones these guides use throughout; see for how to choose ranges:

yaml
		spec:
  clusterNetwork:
    services:
      cidrBlocks: ["10.128.0.0/12"]
    pods:
      cidrBlocks: ["192.168.0.0/16"]
    serviceDomain: "cluster.local"
	
Warning

Pod and Service networks are creation-time settings.

The pod CIDRs, the Service CIDRs, and the service domain cannot be changed once the cluster exists. They are wired into every node, into Cilium, and into the API server, so a later change cannot take effect and is rejected on update. Adding a second range for dual-stack is rejected the same way. If you need something other than the defaults, set it before you create the cluster.

If you do change the ranges, make sure they do not overlap with networks that the cluster must communicate with.

Step 3: Apply the Cluster#

Apply the object to the management cluster:

		$ kubectl apply -f cluster.yaml
cluster.cluster.x-k8s.io/mycluster created
	

This creates the desired state for the workload cluster.

From this point, Syself Autopilot handles the provisioning process. You do not create the individual control plane or worker servers yourself.

Step 4: Watch the cluster come up#

You can watch the Cluster and its Machines from the management cluster:

		$ kubectl get cluster,machines -n my-organization
	

During provisioning, Machines appear as Syself Autopilot creates the requested control plane and worker infrastructure.

For the example cluster, you should eventually see three control plane Machines and three worker Machines:

		$ kubectl get machines -n my-organization
NAME                                  CLUSTER     NODE NAME                             FAILURE DOMAIN   READY   AVAILABLE   UP-TO-DATE   PHASE     AGE   VERSION
mycluster-k9r2f-6g7dx                 mycluster   mycluster-k9r2f-6g7dx                 nbg1             True    True        True         Running   11m   v1.36.3
mycluster-k9r2f-b7pkl                 mycluster   mycluster-k9r2f-b7pkl                 nbg1             True    True        True         Running   10m   v1.36.3
mycluster-k9r2f-r8n4m                 mycluster   mycluster-k9r2f-r8n4m                 nbg1             True    True        True         Running   9m    v1.36.3
mycluster-general-8jq2f-vd2kp-7bs4n   mycluster   mycluster-general-8jq2f-vd2kp-7bs4n   nbg1             True    True        True         Running   7m    v1.36.3
mycluster-general-8jq2f-vd2kp-d9h2p   mycluster   mycluster-general-8jq2f-vd2kp-d9h2p   nbg1             True    True        True         Running   7m    v1.36.3
mycluster-general-8jq2f-vd2kp-q6m8k   mycluster   mycluster-general-8jq2f-vd2kp-q6m8k   nbg1             True    True        True         Running   6m    v1.36.3
	

Each Machine represents one server being managed as part of the workload cluster.

You can continuously watch provisioning with:

		$ kubectl get machines -w
	

Provisioning includes more than creating the servers. Syself Autopilot installs Syself Linux, initializes the control plane, joins additional control plane and worker nodes, establishes networking and firewall rules, installs the required in-cluster components, and waits for the cluster to become operational.

Step 5: Verify the cluster#

Once the Machines are running, retrieve the workload cluster kubeconfig:

		$ kubectl get secret mycluster-kubeconfig \
    -n my-organization \
    -o jsonpath='{.data.value}' \
  | base64 -d \
  > mycluster-kubeconfig.yaml
	

Use it to check the new workload cluster:

		$ KUBECONFIG=./mycluster-kubeconfig.yaml kubectl get nodes
NAME                                  STATUS   ROLES           AGE   VERSION
mycluster-k9r2f-6g7dx                 Ready    control-plane   8m    v1.36.3
mycluster-k9r2f-b7pkl                 Ready    control-plane   7m    v1.36.3
mycluster-k9r2f-r8n4m                 Ready    control-plane   6m    v1.36.3
mycluster-general-8jq2f-vd2kp-7bs4n   Ready    <none>          5m    v1.36.3
mycluster-general-8jq2f-vd2kp-d9h2p   Ready    <none>          5m    v1.36.3
mycluster-general-8jq2f-vd2kp-q6m8k   Ready    <none>          4m    v1.36.3
	

All requested nodes should eventually report Ready.

You can also check the platform pods:

		$ KUBECONFIG=./mycluster-kubeconfig.yaml kubectl get pods -A
	

Some components may still be starting immediately after the nodes first register. The cluster is fully initialized once the requested nodes are Ready and the core platform components have become healthy.

Your workload cluster is now running and continuously managed by Syself Autopilot.

What happens after you apply the Cluster#

The Cluster object remains the desired state of the cluster after creation.

Changing supported fields later can cause Syself Autopilot to reconcile the infrastructure accordingly.

For example:

  • Increasing a worker pool's replicas creates more workers.
  • Decreasing replicas removes workers.
  • Changing a machine type rolls the affected Machines.
  • Moving to a new Cluster Stack release starts the supported platform upgrade process.
  • Adding another MachineDeployment creates another worker pool.

You do not need to rerun a cluster creation procedure. You update the same declarative object.

See for the reconciliation model.

Next steps#

Once the cluster is running, continue with the configuration that applies to your environment:

The Cluster object remains the central place for cluster-level and infrastructure-level configuration throughout the workload cluster's lifecycle.