Skip to main content
The Generic provider for Managed Kubernetes allows Linux-based servers to act as worker nodes of a Kubernetes cluster, irrespective of their deployment in public clouds (such as AWS, Azure, GCP, Hetzner, etc.) or in private cloud or on-premises environments. Once integrated, these servers join a Kubernetes cluster as worker nodes, enabling the deployment of Kubernetes workloads through the Kubernetes API. The provider is vendor-agnostic: a physical server in your data center, a virtual machine on VMware or vSphere, and a cloud instance all join the same way.
This page builds the Kubernetes cluster. To run Control Plane workloads on it, the cluster also needs a location; Deploy a Workload to Your Own Hardware covers both steps.

Supported Add-Ons

  • Headlamp: Provides a Kubernetes dashboard UI for the cluster.
  • AWS Workload Identity: Allows your pods to assume AWS IAM Roles.
  • AWS ECR: Allows pulling images from private ECR registries.
  • Local Path Storage: Creates PVCs backed by local volumes.
  • Logs: Enables logging for pods and cluster auditing.
  • CPLN Platform: Installs a BYOK location into the cluster, so Control Plane workloads run on its servers.

Step 1 - Server Setup

  1. Prepare the servers: Create at least one server that meets the following server requirements:
    • Server requirements:
      • Linux kernel version higher than 5.4
      • Minimum CPU: 1 core
      • Minimum RAM: 512 MB
    • Servers require internet egress access.
    • Ensure you have SSH access to the servers.

Step 2 - Create a Managed Kubernetes Cluster with a Manifest

  1. Optionally update the manifest below and save it as generic-mk8s-template.yaml.
YAML
This example creates a generic cluster with the following configurations:
  • Add-ons: Includes Headlamp, Local Path Storage, and AWS Workload Identity.
  • Location: The cluster’s Kubernetes control plane will be managed in the gcp-us-east1 location. Placing worker nodes close to the control plane is recommended for optimal performance.
  • Kubernetes API Firewall: Uses the default rule, allowing public access to the Kubernetes API. It is advisable to restrict API access to a known IP range for security purposes.
  • Kubernetes Version: 1.35.3.
  • Node Pool: A single general node pool.
  1. Create the Cluster: Deploy the generic-mk8s-example cluster by applying the manifest. Wait until the cluster is initialized: rerun cpln mk8s health generic-mk8s-example until it reports READY as true.

Step 3 - Connect the Servers

  1. Obtain the Join Script:
  2. Copy and Execute the Script on the Server: Connect to the remote server and execute the join script. After the execution, the server should be joined to the cluster as a node under the general node pool. Repeat these steps for all necessary servers.

Step 4 - Accessing the Cluster

1. Using the Terminal

  1. Obtain the cluster’s kubeconfig file: Execute the command cpln mk8s kubeconfig generic-mk8s-example -f /tmp/generic-mk8s-example-conf.
  2. Access the cluster with kubectl: Use the obtained kubeconfig file by running export KUBECONFIG=/tmp/generic-mk8s-example-conf for the current shell session.

2. Using Headlamp

  1. Navigate to the Control Plane Console: Visit the Control Plane Console.
  2. Open Headlamp: In the Control Plane Console, navigate to Kubernetes in the left sidebar panel and click on Headlamp under Dashboard for the cluster generic-mk8s-example.

Advanced Configuration Options

Networking

By default, the cluster uses k3s’ default pod and service network CIDRs. Override spec.provider.generic.networking if either range conflicts with a network your servers already use:
serviceNetwork and podNetwork must be different from each other, and neither can be changed after the cluster is created.
YAML

Ingress IPs

Ingress traffic reaches the cluster through MetalLB. Each node advertises the addresses of its network interfaces as a MetalLB address pool. By default only public IPv4 addresses are included. Private addresses (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) and Tailscale addresses (100.64.0.0/10) are skipped unless the node carries the matching label: Set the labels on a node pool so every server that joins the pool gets them. Node pool labels are applied only when a server runs the join script, so add the labels to servers that already joined with kubectl. The node picks up the change within about 30 seconds.
YAML

Overriding the Ingress IP

To advertise a specific ingress IP (or hostname) for an availability zone, set the INGRESS_IP_<ZONE> environment variable on the actuator through the CPLN Platform add-on at spec.addOns.byok.config.actuator.env. <ZONE> is the value of the nodes’ topology.kubernetes.io/zone label, converted to upper snake case (for example, az1 becomes AZ_1 and us-east-1a becomes US_EAST_1_A). When the variable is not set, the actuator uses the address of the zone’s ingress load balancer.
YAML
Generic nodes have no zone label when they join. After 5 minutes, the actuator labels each unzoned node with the zone of another node in the cluster, or az1 when no node has one, so the default variable is INGRESS_IP_AZ_1. To use your own zone names, add topology.kubernetes.io/zone to the node pool labels. See Actuator Settings for other actuator environment variables.