kind

The kind binary — and, with a config blob, a running Kubernetes cluster with ingress and a load balancer.

kind runs Kubernetes in Docker. This recipe has two modes.

Without a config blob it installs the static kind binary and stops there:

devtools:
  - name: docker
  - name: kind

With a config blob it also stands the cluster up during provisioning — creating a named cluster, installing an ingress controller, and installing a LoadBalancer provider — so the workstation arrives at Configured with a working cluster you can kubectl against immediately.

devtools:
  - name: docker
  - name: kind
    config:
      cluster: dev
      ingress: nginx
      loadBalancer: cloud-provider-kind
      expose:
        - { port: 80, hostPort: 8080 }

Always declare docker before kind. The recipe waits for the Docker daemon to answer before creating the cluster, and installs kubectl itself if it is not already present.

version

Selects the kind binary release (for example v0.24.0). Omit it for the recipe’s pinned default.

config fields

FieldTypeDefaultDescription
clusterstringkindThe cluster name.
ingressstringnginxnginx installs the ingress-nginx controller bound to the node’s ports 80/443. none skips it.
loadBalancerstringcloud-provider-kindThe provider for type: LoadBalancer services: cloud-provider-kind, metallb, or none.
exposearray{port, hostPort} pairs. Each maps a container port on the cluster node to a port on the VM, listening on all interfaces.

expose and reaching the cluster from your laptop

expose is how traffic gets in. Each entry publishes a node port as a VM-wide listener, which a LocalBinding with autoForward then forwards to your machine. The reachable path is the mapped ingress port, not a LoadBalancer IP: expose the ingress controller’s port 80 to a VM port, forward that, and your ingress routes resolve on localhost.

Load balancer providers

  • cloud-provider-kind (default) watches the Docker API and provisions a proxy container per type: LoadBalancer service. It is installed as a managed service and needs no address-pool configuration.
  • metallb installs MetalLB and derives an L2 address pool from the cluster’s Docker network subnet.
  • none installs neither. type: LoadBalancer services stay pending — fine when you reach everything through the ingress.

Example

A workstation that builds an image, loads it into a local cluster, and serves it through the ingress on your laptop’s localhost:8080:

apiVersion: workstations.ringleader.dev/v1
kind: WorkstationConfig
metadata:
  name: kind-box
  namespace: local
spec:
  selector:
    matchLabels:
      app: kind-demo
  identity:
    user: dev
    shell: /bin/bash
    sudo: true
  packages:
    - git
    - curl
  devtools:
    - name: docker
    - name: kind
      config:
        cluster: demo
        ingress: nginx
        loadBalancer: none        # ingress-only: no LoadBalancer provider needed
        expose:
          - { port: 80, hostPort: 8080 }
  defaultLocalBinding:
    enabled: true
    autoForward:
      forwardAll: true
---
apiVersion: workstations.ringleader.dev/v1
kind: Workstation
metadata:
  name: kind-demo-box
  namespace: local
  labels:
    app: kind-demo
spec:
  # A live cluster plus an image build needs real headroom.
  providerConfig:
    memory: 8
    cpus: 4

Then curl http://127.0.0.1:8080 on your laptop reaches whatever your ingress routes.

When it counts as installed

For a configured cluster, “installed” means the binary exists and the named cluster exists. If the cluster is destroyed out-of-band — a docker system prune, a wiped Docker data root — the next setup run recreates it. Creating a cluster that already exists is skipped; the ingress and load-balancer steps are safe to re-apply.

Notes

  • The login user gets a kubeconfig pointing at the cluster, so kubectl works from an interactive shell without sudo.
  • The cluster’s ingress controller can take a few minutes to pull its image on a slow link. That wait is a logged note, not a failure.
  • Sizing matters: a control-plane node image plus ingress plus your own workloads needs several GiB of RAM. providerConfig lives on the Workstation, not on a config layer.