Unit 6.2: Provisioning a Public Cluster
Introduction
Unit 6.1 settled the design decisions: a free managed control plane over paid node pools, no scale-to-zero, security groups bound to worker NICs rather than the cluster, and the hard truth that a LoadBalancer Service is not a managed external load balancer. This unit realises a public cluster in the Data Center Designer. The build is short; the consequential decisions are the node-pool server type and version (which fix things you cannot change later), how persistent storage is provisioned through the CSI driver, and how traffic actually reaches your pods. FinCorp needs a public-facing cluster to host the stateless API tier that fronts its new AI capability, so we build exactly that.
1. The decisions the build fixes
Two choices made in the create wizard are effectively permanent. The node-pool type cannot be switched between public and private after creation, and a public node pool is what allows a LoadBalancer Service type at all (private pools do not support it). FinCorp's API tier is internet-facing, so a public pool is correct here; the regulated data tier stays on the private cluster built in Unit 6.3.
The server type is chosen per node pool from Dedicated Core or vCPU. CPU resource mapping is fixed: one provisioned core equals two Managed Kubernetes CPUs. The recommended ceiling is 20 nodes per node pool (hard maximum 100), and a node carries up to 110 pods and up to 20 attached volumes. FinCorp uses Dedicated Core for the API tier to get an exclusive core and predictable scheduling under load.
Control-plane placement decides sovereignty. For a public cluster the IONOS-managed control plane runs in Frankfurt or one of three US datacenters (Lenexa, Newark, Las Vegas); choosing Frankfurt keeps control-plane data in Germany, the only acceptable option for FinCorp under BSI and GDPR. Node-pool workloads and data always remain in the chosen customer region regardless. Note also the honest boundaries from 6.1: BSI attestations cover the IONOS infrastructure layer, not the workloads on the cluster, and Kubernetes control-plane events do not flow through the IONOS Logging Service.
2. Persistent storage through the CSI driver
The cluster provisions Block Storage volumes as Kubernetes Persistent Volumes through the IONOS CSI driver, whose provisioner is cloud.ionos.com. You control placement and type by defining a StorageClass. The example below pins SSD storage to an availability zone and enables online expansion:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ionos-enterprise-ssd-zone-1
provisioner: cloud.ionos.com
parameters:
type: SSD
fstype: ext4
availabilityZone: ZONE2
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
WaitForFirstConsumer delays binding until a pod is scheduled, so the volume lands in the same zone as the consuming node. Dynamically provisioned volumes are managed by the CSI driver: with the Retain reclaim policy a volume survives PV deletion and appears as a leftover volume in the VDC, so reclaim policy is a deliberate lifecycle decision, not a default to ignore.
3. Ingress: there is no automatic managed load balancer
This is the boundary that most often surprises teams arriving from a hyperscaler. A Service of type LoadBalancer does not deploy a true external load balancer in front of the cluster. IONOS allocates a static public IP and assigns it as a secondary IP to a single worker node, which becomes the ingress node; if the target pod runs elsewhere, kube-proxy NATs the traffic to it. Two consequences follow. Throughput is bounded by that one node's public ceiling, so to scale beyond it you reserve multiple IPs and spread them across multiple ingress nodes (DNS load balancing). And the NAT replaces the source IP, so the client address is lost unless you set externalTrafficPolicy: Local.
The production pattern is therefore to expose a single in-cluster ingress controller as the LoadBalancer Service and let it route HTTP inside the cluster, rather than exposing each application Service. Reserve the ingress IP in IP Management outside Kubernetes so it is not released when the Service is deleted, then pin it to a dedicated ingress node.
Where FinCorp needs genuine managed Layer 7 in front of the cluster, the answer is a separately provisioned Managed Application Load Balancer (Unit 3.3), not a manifest. The ALB requires a reserved public IP, terminates TLS (its listener accepts exactly one leaf certificate, optionally with its CA chain in the same file), and is wired to the cluster nodes as targets. Nothing in a Kubernetes manifest provisions or configures it; it is a distinct resource you build and point at the ingress nodes.
DCD Implementation Walkthrough
You will build a public Managed Kubernetes cluster with one Dedicated Core node pool, retrieve its kubeconfig, define a CSI storage class, and front it with an ingress controller exposed through a reserved IP. Prerequisite: the Create Kubernetes Clusters permission (contract owners, administrators, and granted users), and a reserved public IPv4 in IP Management for the ingress endpoint.
Build goal: Build a public cluster end-to-end.
Steps (in the Data Center Designer):
- Go to Menu > Containers > Managed Kubernetes, then select + Create Cluster. Enter a cluster name following Kubernetes naming conventions and pick the Kubernetes version. For FinCorp, select a Frankfurt-backed control plane to keep control-plane data in Germany.
- Open the new cluster and go to the Node pools in Cluster tab, then select Create node pool.
- In Pool Settings, enter a Pool Name, select the Data Center the nodes live in (create one first if needed), choose the Node pool version, and set the Node count.
- Optionally enable Autoscale and provide a minimum and maximum node count. The floor is one warm node; there is no scale-to-zero.
- In the Node Template, set Server type to Dedicated Core (the default) or vCPU, then choose Cores, RAM, and Availability Zone. Remember one provisioned core maps to two Managed Kubernetes CPUs.
- Under Reserved IPs, add the reserved public IP that will back the ingress endpoint, and attach any required private LANs. Provision the node pool and wait for the nodes to reach ACTIVE.
- Retrieve access: in the Cluster Settings tab, download
kubeconfig.yaml(or.json), or use the CLI:ionosctl k8s kubeconfig get --cluster-id CLUSTERID. Pointkubectlat the file. - Apply the CSI StorageClass from Section 2 (
provisioner: cloud.ionos.com), then deploy an ingress controller and expose only its Service as typeLoadBalancer, pinned to your reserved IP. Place application Services behind that controller, not directly on the internet.
Common mistakes:
- Treating a
LoadBalancerService as a managed LB. It is a single-node static IP with kube-proxy NAT, capped at one node's throughput. Expose only the ingress controller this way; use a separately provisioned Managed ALB for real Layer 7 in front of the cluster. - Forgetting
externalTrafficPolicy: Localwhen the application needs the real client IP. The default NAT path discards the source address. - Letting Kubernetes auto-reserve the ingress IP. Reserve it in IP Management first so deleting the Service does not release the address (and break your DNS records).
- Choosing the wrong node-pool type. Public and private cannot be switched after creation; a private pool would not support the
LoadBalancerService at all. - Placing the control plane in a US datacenter for a regulated workload. For a public cluster, choose Frankfurt to keep control-plane data in Germany.
- Expecting cluster logs and control-plane events in the Logging Service. Control-plane events do not flow there; plan separate forwarding.
Summary
A public Managed Kubernetes cluster is a short build whose weight sits in a few irreversible choices: node-pool type and server type, control-plane placement for sovereignty, and how persistent storage and ingress are actually realised. Storage is dynamic through the cloud.ionos.com CSI driver via a StorageClass you define; ingress is not automatic, so you front the cluster with an in-cluster ingress controller on a reserved IP and reach for a separately provisioned Managed ALB when you need true managed Layer 7. FinCorp now has its public API tier ready to compose with the private data tier built next.
Key Points:
- A public node pool permits the
LoadBalancerService type; private pools do not, and the type is immutable after creation. - The IONOS CSI driver (
provisioner: cloud.ionos.com) provisions Block Storage as Persistent Volumes; the StorageClass fixes type, zone, expansion, and reclaim behaviour. - A
LoadBalancerService is a single-node static IP with kube-proxy NAT, not a managed external LB; scale across multiple ingress IPs and reserve them outside Kubernetes. - Source IP is lost without
externalTrafficPolicy: Local; a separately provisioned Managed ALB provides real Layer 7 and TLS termination in front of the cluster. - Choose a Frankfurt control plane for public clusters to keep control-plane data in Germany; control-plane events do not reach the Logging Service.
Important Terminology:
- CSI driver: the Container Storage Interface plugin (
cloud.ionos.com) that dynamically provisions IONOS Block Storage as Kubernetes Persistent Volumes. - Ingress node: the single worker node that receives a
LoadBalancerService's static IP as a secondary IP and forwards traffic via kube-proxy. - StorageClass: the Kubernetes object that defines how persistent volumes are provisioned (type, availability zone, file system, expansion, binding mode).
Further Reading
- Unit 6.1: Kubernetes Platform Design (the design decisions this build realises)
- Unit 6.3: Provisioning a Private Cluster (the networking-first private variant)
- Unit 3.3: Load Balancing - Layer 7 (Application) (the Managed ALB front)