Bring Your Own Cluster

Add Unbounded to an existing Kubernetes cluster and join remote nodes.

This guide adds Unbounded to a Kubernetes cluster you already have running. You’ll label gateway nodes, initialize a site, and join remote machines.

Starting from scratch? See the Getting Started guide to create an AKS cluster with everything pre-configured.

What You’ll Do

  1. Install the prerequisites – kubectl and the unbounded plugin
  2. Prepare gateway nodes – label and open WireGuard ports
  3. Initialize a site – install the networking stack and create site resources
  4. Add machines – register remote hosts for SSH provisioning
  5. Watch progress – monitor the provisioning lifecycle

1. Install the Prerequisites

You’ll need: A Kubernetes cluster with kubeconfig access, one or more cluster nodes with UDP 51820-51899 open, and remote machines reachable via SSH.

Note

Clusters with an existing CNI (e.g. Cilium, Calico, Azure CNI): Unbounded works alongside your existing CNI. Pass --manage-cni-plugin=false when running site init, or set manageCniPlugin: false on the Site resource after creation. See the manageCniPlugin reference for details.

Clusters without a CNI (created with --network-plugin none): No extra configuration is needed — unbounded-net will serve as the CNI.

Install the kubectl-unbounded plugin:

# Linux (amd64)
curl -sL https://github.com/Azure/unbounded/releases/latest/download/kubectl-unbounded-linux-amd64.tar.gz | tar xz
sudo mv kubectl-unbounded /usr/local/bin/
macOS (Apple Silicon)
curl -sL https://github.com/Azure/unbounded/releases/latest/download/kubectl-unbounded-darwin-arm64.tar.gz | tar xz
sudo mv kubectl-unbounded /usr/local/bin/

Verify:

kubectl unbounded --help

2. Prepare Gateway Nodes

At least one cluster node must be labeled as a WireGuard gateway. Open UDP 51820-51899 on the node’s firewall, then label it:

kubectl label node <node-name> "unbounded-cloud.io/unbounded-net-gateway=true"
Important
kubectl unbounded site init checks for the gateway label and fails if no labeled nodes are found. You must also ensure UDP ports 51820-51899 are open on the gateway node’s firewall before proceeding – the init command does not open these ports for you.

3. Initialize a Site

A Site represents a remote location where machines will run. The site init command installs unbounded-net, creates site resources, generates a bootstrap token, and deploys the machina controller – all in one step.

kubectl unbounded site init \
    --name my-site \
    --cluster-node-cidr 10.224.0.0/16 \
    --cluster-pod-cidr 10.244.0.0/16 \
    --node-cidr 192.168.1.0/24 \
    --pod-cidr 10.245.0.0/16
FlagDescription
--nameName of the remote site
--cluster-node-cidrCIDR used by the cluster for node IPs
--cluster-pod-cidrCIDR used by the cluster for pod IPs
--node-cidrSubnet of your remote machines (e.g. 192.168.1.0/24). Must not overlap with the cluster’s node CIDR.
--pod-cidrPod CIDR for the remote site (e.g. 10.245.0.0/16). Must not overlap with the cluster’s pod or service CIDRs.
Optional flags
FlagDescription
--kubeconfigPath to kubeconfig file
--cni-manifestsPath or URL to CNI manifests (uses embedded manifests if omitted)
--machina-manifestsPath or URL to machina manifests (uses embedded manifests if omitted)
--manage-cni-pluginSet to false when the cluster already has a CNI (default: true)
Tip
AKS users: The aks-quickstart.sh setup command can auto-detect your cluster’s CIDRs and add a properly configured gateway node pool. See the Getting Started guide for details.

4. Add Machines

Register remote machines with machine register. Each machine must be reachable via SSH:

kubectl unbounded machine register \
    --site my-site \
    --host 10.0.0.5 \
    --ssh-username ubuntu \
    --ssh-private-key ~/.ssh/id_rsa
FlagDescription
--siteSite name (must already be initialized)
--hostHost IP, optionally with port (10.0.0.5 or 10.0.0.5:2222)
--ssh-usernameSSH username
Optional flags
FlagDescription
--nameMachine name (derived from host if omitted)
--ssh-private-keyPath to SSH private key
--ssh-secret-nameK8s secret name for SSH credentials (defaults to ssh-$site)
--bastion-hostBastion host and optionally port
--bastion-ssh-usernameBastion SSH username (defaults to --ssh-username)
--bastion-ssh-private-keyBastion SSH key (defaults to --ssh-private-key)
--bastion-ssh-secret-nameBastion SSH secret (defaults to --ssh-secret-name)
--kubeconfigPath to kubeconfig file

5. Watch Progress

Machines transition through phases as they are provisioned:

PendingProvisioningJoiningReady

watch 'kubectl get machines'

Next Steps