kind

A local Kubernetes cluster on the workstation, with an ingress controller and a load balancer, reachable from your laptop.

kind runs Kubernetes in Docker. Declared with a config block, this devtool creates a cluster while the workstation sets itself up, so the workstation arrives at Configured with a cluster you can kubectl against, and an ingress you can reach from your laptop:

spec:
  devtools:
    - name: docker
    - name: kind
      config:
        cluster: dev
        expose:
          - { port: 80, hostPort: 8080 }
  defaultLocalBinding:
    enabled: true
    autoForward:
      forwardAll: true

Then curl http://127.0.0.1:8080 on your laptop reaches whatever your ingress routes. Declare docker first, since kind needs it; the recipe waits for the Docker daemon before creating the cluster and installs kubectl if it is not already there.

Without a config block the devtool installs only the kind binary, for you to create clusters yourself.

The rest of this page covers the fields, how traffic gets in, load balancers, and sizing.

config fields

FieldTypeDefaultDescription
clusterstringkindThe cluster name.
ingressstringtraefiktraefik installs the Traefik ingress controller, bound to the node’s ports 80 and 443. none skips it. nginx is still accepted for older manifests and installs Traefik too, because ingress-nginx is no longer maintained.
loadBalancerstringcloud-provider-kindWhat backs type: LoadBalancer services: cloud-provider-kind, metallb, or none.
exposearraynone{port, hostPort} pairs. Each publishes a port on the cluster node as a port on the workstation, listening on all of its interfaces.

version selects the kind binary release (for example v0.33.0); omit it for the pinned default, v0.33.0 today.

Reaching the cluster from your laptop

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

Load balancer providers

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

Example

A workstation that builds an image, loads it into the 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: traefik
        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

Notes

  • Sizing matters: a control-plane node, an ingress controller and your own workloads need several GiB of RAM. providerConfig lives on the Workstation, not on a config layer.
  • The login user gets a kubeconfig for the cluster, so kubectl works from a shell without sudo.
  • The ingress controller can take a few minutes to pull its image on a slow link. That wait is logged, not treated as a failure.
  • For a configured cluster, “installed” means the binary exists and the named cluster exists. If the cluster is destroyed from outside (a docker system prune, a wiped Docker data root), the next setup run recreates it. Creating a cluster that already exists is skipped, and the ingress and load-balancer steps are safe to re-apply.