Deploy a Kubernetes Cluster¶
This guide covers two paths: provisioning a new Gardener-managed cluster and registering an existing cluster via kubeconfig. Both end with a verified connection and a running workload.
Time to complete: ~20 minutes (Gardener), ~5 minutes (generic)
Prerequisites: An organization, a project, and a bearer token with garden:create permission.
Path A — Gardener-managed cluster¶
Step 1 — Discover available options¶
The response lists available Kubernetes versions, machine types, and regions. Note the values you want for version, cloud, and machine_type.
Step 2 — Create the cluster¶
POST /k8s/clusters?project_id=<uuid>
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "prod-cluster",
"project_id": "<uuid>",
"cloud": "openstack",
"version": "1.29",
"node_pools": [
{
"name": "workers",
"machine_type": "m1.large",
"min": 2,
"max": 5
}
]
}
Returns 201 Created. Note the cluster id.
Step 3 — Wait for the cluster to be ready¶
Poll until creating: false and status.lastOperation.state is "Succeeded":
Provisioning typically takes 5–10 minutes. While it runs, creating: true and lastOperation tracks progress.
Step 4 — Download the kubeconfig¶
Once the cluster is ACTIVE, download the kubeconfig from the dashboard or through the Gardener API (available via the dashboard's Download kubeconfig button). The api_server field in the cluster show response gives you the Kubernetes API server URL.
Step 5 — Deploy a workload¶
export KUBECONFIG=~/Downloads/kubeconfig-prod-cluster.yaml
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# worker-abc123 Ready <none> 3m v1.29.4
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-world
spec:
replicas: 2
selector:
matchLabels:
app: hello-world
template:
metadata:
labels:
app: hello-world
spec:
containers:
- name: hello
image: nginx:alpine
ports:
- containerPort: 80
EOF
kubectl rollout status deployment/hello-world
Path B — Register an existing cluster (generic)¶
Use this path to bring an existing cluster (k3s, EKS, GKE, on-prem) under platform management.
Step 1 — Register the cluster¶
POST /k8s/clusters?project_id=<uuid>
Authorization: Bearer <token>
Content-Type: application/json
{
"kind": "generic",
"name": "on-prem-k3s",
"kubeconfig": "<raw-kubeconfig-yaml-or-base64>"
}
The kubeconfig field is write-only — it is never returned in responses. The response includes has_kubeconfig: true to confirm it was stored.
Step 2 — Verify connectivity¶
The connection_status field reports reachable or unreachable based on an API server probe. If unreachable, check that the cluster's API server is publicly accessible or that you have provided a kubeconfig with the correct server URL.
Step 3 — Rotate the kubeconfig¶
When credentials expire or you rotate your cluster's service account:
PATCH /k8s/clusters/<cluster-id>?project_id=<uuid>
Authorization: Bearer <token>
Content-Type: application/json
{ "kubeconfig": "<new-kubeconfig-yaml>" }
Scale node pools (Gardener clusters only)¶
To scale an existing node pool:
PATCH /k8s/clusters/<cluster-id>?project_id=<uuid>
Authorization: Bearer <token>
Content-Type: application/json
{
"node_pools": [
{ "name": "workers", "min": 3, "max": 10 }
]
}
Gardener applies the change asynchronously. Watch lastOperation to track rollout progress.
Delete a cluster¶
For Gardener clusters, this triggers deprovisioning of the underlying cloud resources. The cluster disappears from list results once teardown is complete.
Using the dashboard¶
The dashboard at app.bnerd.cloud provides a visual Kubernetes management interface:
- Create a cluster: Services → Kubernetes → New Cluster
- Download kubeconfig: Cluster detail → Download kubeconfig button
- Scale node pools: Cluster detail → Node Pools tab → edit min/max
- Delete a cluster: Cluster detail → Delete Cluster
Using the CLI¶
# List cloud profiles (available versions, machine types)
bnerd k8s cloud-profiles list
# List clusters
bnerd k8s clusters list
# Create a Gardener-managed cluster
bnerd k8s clusters create prod-cluster \
--version 1.29 \
--cloud openstack \
--machine-type m1.large \
--min 2 \
--max 5
# Show cluster status
bnerd k8s clusters get <cluster-id>
# Delete a cluster
bnerd k8s clusters delete <cluster-id>
See cli.docs.bnerd.com for the full flag reference.
Terraform¶
Kubernetes cluster management via Terraform is available via data sources for reading existing clusters; full lifecycle management (create/delete) is not yet covered by the Terraform provider — use the API or CLI for cluster provisioning:
# Read an existing cluster
data "bnerd_k8s_cluster" "prod" {
id = var.cluster_id
project_id = var.project_id
}
# List cloud profiles
data "bnerd_k8s_cloudprofiles" "all" {}
Troubleshooting¶
| Symptom | Likely cause | Fix |
|---|---|---|
Cluster stuck in creating: true > 15 min | Gardener provisioning delay or resource limit | Check lastOperation.description in the show response; contact support if stuck > 30 min |
kubectl get nodes returns no nodes | kubeconfig is stale or node pool has 0 replicas | Re-download the kubeconfig; verify min ≥ 1 on the node pool |
connection refused on kubeconfig download | Cluster API server not yet ready | Wait for lastOperation.state: "Succeeded" before attempting to download |
403 on cluster create | Missing garden:create permission | Check your membership permissions in Dashboard → Settings → Members |
generic cluster shows unreachable | API server not publicly accessible | Ensure the cluster API server URL in the kubeconfig is reachable from the platform |
Related pages¶
- Clusters
- Node Pools
- Object Storage (S3 Operator) — provision storage for your workloads