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
- Install the prerequisites – kubectl and the unbounded plugin
- Prepare gateway nodes – label and open WireGuard ports
- Initialize a site – install the networking stack and create site resources
- Add machines – register remote hosts for SSH provisioning
- Watch progress – monitor the provisioning lifecycle
1. Install the Prerequisites
You’ll need: A Kubernetes cluster with
kubeconfigaccess, one or more cluster nodes with UDP 51820-51899 open, and remote machines reachable via SSH.
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"
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
| Flag | Description |
|---|---|
--name | Name of the remote site |
--cluster-node-cidr | CIDR used by the cluster for node IPs |
--cluster-pod-cidr | CIDR used by the cluster for pod IPs |
--node-cidr | Subnet of your remote machines (e.g. 192.168.1.0/24). Must not overlap with the cluster’s node CIDR. |
--pod-cidr | Pod 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
| Flag | Description |
|---|---|
--kubeconfig | Path to kubeconfig file |
--cni-manifests | Path or URL to CNI manifests (uses embedded manifests if omitted) |
--machina-manifests | Path or URL to machina manifests (uses embedded manifests if omitted) |
--manage-cni-plugin | Set to false when the cluster already has a CNI (default: true) |
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
| Flag | Description |
|---|---|
--site | Site name (must already be initialized) |
--host | Host IP, optionally with port (10.0.0.5 or 10.0.0.5:2222) |
--ssh-username | SSH username |
Optional flags
| Flag | Description |
|---|---|
--name | Machine name (derived from host if omitted) |
--ssh-private-key | Path to SSH private key |
--ssh-secret-name | K8s secret name for SSH credentials (defaults to ssh-$site) |
--bastion-host | Bastion host and optionally port |
--bastion-ssh-username | Bastion SSH username (defaults to --ssh-username) |
--bastion-ssh-private-key | Bastion SSH key (defaults to --ssh-private-key) |
--bastion-ssh-secret-name | Bastion SSH secret (defaults to --ssh-secret-name) |
--kubeconfig | Path to kubeconfig file |
5. Watch Progress
Machines transition through phases as they are provisioned:
Pending → Provisioning → Joining → Ready
watch 'kubectl get machines'
Next Steps
- Project Overview – how the components fit together
- SSH Guide – bastion hosts, troubleshooting, and the full provisioning lifecycle
- PXE Guide – boot bare-metal servers with metalman
- Networking Concepts – cross-site pod networking deep dive
- CLI Reference – all
kubectl unboundedcommands and flags - CRD Reference – Machine and Image API specification