Overview
From ISO to running cluster
Every Ruckos machine starts from the same ISO. Once it boots, you decide what it becomes: a hypervisor that runs VMs, or a Kubernetes node. There is no SSH and no package manager. You manage nodes through their API with one of three tools.
- rctl — the command line. Everything can be done with it, and it scripts well.
- Ruckos-V — the Windows desktop app for hypervisors, VMs and hypervisor clusters.
- Lighthouse — the web console for running many Kubernetes clusters from one place.
Get access
Early access
The Ruckos ISO is not public yet. Contact us to sign up for early access and we will send you the download.
Contact us for accessSystem requirements
Baseline
- UEFI-capable bare-metal host or VM.
- A network with DHCP for first boot. Static addresses are set with rctl.
- A disk for Ruckos. Hypervisors also need a disk or free space for VMs.
- Active Directory DNS and AD CS for the full Ruckos Cloud platform.
- Registry access for workload images.
Quick start
Boot, scan, pick a path
- Boot each machine from the Ruckos ISO. It comes up in maintenance mode and waits on the network.
- Find it from your workstation.
rctl scan
Virtualization
Turn hosts into hypervisors, cluster them, and create VMs.
rctl bootstrap hypervisor 192.168.1.209 --cluster lab
rctl bootstrap hypervisor 192.168.1.210 --cluster lab --join 192.168.1.209
rctl vm create 192.168.1.209 --name web1 --cpus 2 --memory 4G --disk 40G --iso ./installer.iso
Ruckos-V does the same steps from a Windows desktop.
Kubernetes
Give each node a role, write the site file, and bootstrap the cluster in one command.
rctl scan --range 192.168.1.0/24 --save
rctl assign 192.168.1.48 --role controlplane --cluster prod
rctl assign 192.168.1.49 --role worker --cluster prod
rctl write-config --site config/site.yaml --cluster prod
rctl bootstrap --site config/prod/prod.yaml --type stacked
rctl get-nodes --cluster prod
Add rctl bootstrap platform for the full Ruckos Cloud stack, and rctl bootstrap lighthouse to manage it from the web.
Which tool when
Common questions
Which tool should I start with?
Use rctl if you want to script it or work from any operating system. Use Ruckos-V if you run VMs and want a desktop app on Windows. Add Lighthouse once you run more than one Kubernetes cluster and want them in one place.
Can I mix them?
Yes. All three talk to the same node API, so a host you set up with
rctl shows up in Ruckos-V, and the other way around. Lighthouse
imports the same site file: rctl export-site --site config/site.yaml
writes it as JSON, and you import that under Config in Lighthouse.
Do I need Kubernetes to run VMs?
No. A hypervisor runs VMs without Kubernetes. Kubernetes nodes can run as VMs on your hypervisors, or on bare metal.
Stacked or external etcd?
Stacked runs etcd on the control-plane nodes, so you
need fewer machines: --type stacked.
External runs etcd on its own nodes with
--role etcd: --type external. rctl
checks that the site file matches the type you pick.
How do I add a node later?
Boot it from the ISO, then rctl scan --save and
rctl assign it. For Kubernetes, run
rctl write-config --append and
rctl bootstrap workers --site <file>. For a
hypervisor cluster, run
rctl bootstrap hypervisor <ip> --cluster lab --join <host-ip>.
How many hypervisors run the cluster database?
The cluster picks it for you: one host for 1–2 hosts, three for
3–4, five for 5 or more. rctl cluster status shows
which hosts they are.
How do I move a VM to another host?
rctl vm move <host-ip> <name> lists the hosts
that can take it. Add --to <hostname> to move it,
and --live to keep it running while it moves.
How do I take a host out of a hypervisor cluster?
For a host that is running, use rctl cluster leave <host-ip>;
its VMs keep running. For a host that is switched off, use
rctl cluster remove <host-ip> <hostname> from
any live host. In Ruckos-V, both are on the Ruckos Cluster tab in Settings.
Command reference
rctl commands
Run rctl <command> --help for full details and
examples. These flags work on every command:
-n, --nodeNode IP or hostname-p, --portruckos API port (default 8443)-t, --tokenBearer token (or set RCTL_TOKEN env var / rctlconfig)--insecureSkip TLS certificate verification (required for first maintenance-mode apply)--rctlconfigPath to rctl client config (default: RCTLCONFIG, ./rctlconfig, ~/.rctl/config)-o, --outputOutput format: text or json
Discover and configure nodes
rctl scan [--range 192.168.1.0/24]
Scan a network range for ruckos nodes
--forceWith --save: fully overwrite existing JSON files instead of only updating dhcp--rangeCIDR range to scan, e.g. 192.168.1.0/24 (default: auto-detect from local interfaces)--saveWrite config/<hostname>.json for each maintenance-mode node found--timeoutPer-host timeout in milliseconds (covers TCP connect, TLS handshake and the /healthz and /config replies; a loaded node needs well over 500 ms)
rctl get-config
Fetch the node config and save to config/<hostname>.json
--allScan config/ for maintenance-mode nodes and refresh dhcp field on all of them in parallel--dnsOverride DNS servers in saved JSON (comma-separated). Falls back to defaults.dns in rctlconfig.--roleOverride role in saved JSON (e.g. worker, maintenance). Falls back to defaults.role in rctlconfig.
rctl assign <ip> [--role <role>] [--cluster <cluster-name>]
Assign a node's role and cluster
--clusterCluster the node belongs to--roleRole the node is for: controlplane, worker, etcd or hypervisor
rctl set-network
Set static network configuration (IP, gateway, DNS) on a node
--addressStatic IP in CIDR notation, e.g. 192.168.1.50/24 (empty string "" clears it to use DHCP)--dnsDNS server(s); repeat or comma-separate, e.g. --dns 1.1.1.1,8.8.8.8--gatewayDefault gateway, e.g. 192.168.1.1--hostnameNode hostname--interfaceNetwork interface to configure, e.g. eth0 (empty = auto-detect)--rebootReboot the node after saving so the config is applied immediately
rctl apply-config [file]
Push a node config or bootstrap config to the node
rctl write-config
Write config/<cluster>/<cluster>.yaml from a site template and the node JSON configs
--appendAppend to existing nodes list instead of replacing it--clusterCluster name for archiving JSONs (required)--sitePath to the site template (required; read only, never modified)
rctl export-site
Write site.yaml as JSON for import into Lighthouse
--sitePath to site.yaml (required)
rctl gen config <cluster-name> <https://cp-ip:6443>
Generate machine configs for a ruckos cluster
--ad-domainAD DNS domain for CoreDNS forwarding (e.g. corp.example.com)--cluster-dnsCluster DNS service IP (must match kubelet clusterDNS)--control-plane-vipOptional kube-vip VIP fronting :6443 across all control-plane nodes (bare IP, no scheme/port)--control-plane-vip-interfaceNIC name kube-vip binds to on control-plane nodes (required if --control-plane-vip is set)--cp-hostnameControl plane node hostname (default: <cluster-name>-cp, or from node context)--l2-ifaceCilium L2 announcement interface (site networking.l2_announce_iface)--lb-endCilium LB pool end IP (site networking.lb_pool.end)--lb-startCilium LB pool start IP (site networking.lb_pool.start)--node-configPath to a saved 'get-config -o yaml' file to use as node context (hostname, etc.)--node-monitor-grace-periodkube-controller-manager --node-monitor-grace-period override (e.g. 40s). Empty = Kubernetes default.--oidc-client-idOIDC client ID for kube-apiserver--oidc-issuer-urlOIDC issuer URL for kube-apiserver (e.g. https://secrets.owc.cobritek.com/realms/owc)--output-dirDirectory to write generated files--pod-cidrPod CIDR--pod-eviction-secondskube-apiserver --default-not-ready/unreachable-toleration-seconds override. 0 = Kubernetes default (300).--service-cidrService CIDR
rctl gen coredns
Generate a CoreDNS manifest (standalone, for kubectl apply)
--ad-domainAD DNS domain for CoreDNS forwarding (e.g. corp.example.com)--cluster-dnsCluster DNS service IP--outputWrite to file instead of stdout
Bootstrap Kubernetes
rctl bootstrap
Bootstrap Kubernetes on ruckos nodes
--sitePath to site.yaml (one-shot bootstrap)--typeetcd topology: stacked or external (one-shot bootstrap)
rctl bootstrap init
Initialize the first control-plane node of a new cluster
--ad-domainAD DNS domain for CoreDNS forwarding (e.g. owc.cobritek.com)--clusterCluster name (output goes to config/<cluster>/)--cluster-dnsCluster DNS service IP--endpointKubernetes API endpoint (default: https://<static-ip>:6443)--etcd-endpointsExternal etcd endpoints (auto-detected from etcd-members.json if omitted; comma-separated https://host:port)--l2-ifaceCilium L2 announcement interface (site networking.l2_announce_iface)--lb-endCilium LB pool end IP (site networking.lb_pool.end)--lb-startCilium LB pool start IP (site networking.lb_pool.start)--node-monitor-grace-periodkube-controller-manager --node-monitor-grace-period override (e.g. 40s). Empty = Kubernetes default.--oidc-client-idOIDC client ID for kube-apiserver--oidc-issuer-urlOIDC issuer URL for kube-apiserver (e.g. https://secrets.owc.cobritek.com/realms/owc)--pod-eviction-secondskube-apiserver --default-not-ready/unreachable-toleration-seconds override. 0 = Kubernetes default (300).
rctl bootstrap join
Join an additional control-plane node to an existing cluster
--clusterCluster name (output goes to config/<cluster>/)--control-plane-vipControl-plane VIP already fronting this cluster (bare IP, no scheme/port) — must match what the cluster was initialized with, so this node's kubeconfigs/cert SANs/kube-vip manifest stay consistent with its peers--control-plane-vip-interfaceNIC name kube-vip binds to on this node (required if --control-plane-vip is set)--cp1IP of the existing control-plane node (optional — derived from admin.conf if omitted)--initial-clusterSkip etcd member add and use this peer list directly (e.g. "cp1=https://IP1:2380,cp2=https://IP2:2380")--l2-ifaceCilium L2 announcement interface (site networking.l2_announce_iface)--lb-endCilium LB pool end IP (site networking.lb_pool.end)--lb-startCilium LB pool start IP (site networking.lb_pool.start)--node-monitor-grace-periodkube-controller-manager --node-monitor-grace-period override — must match what the cluster was initialized with--pod-eviction-secondskube-apiserver --default-not-ready/unreachable-toleration-seconds override — must match what the cluster was initialized with
rctl bootstrap worker
Provision a worker node end-to-end from a saved JSON config
--clusterCluster name (reads worker.yaml from config/<cluster>/)
rctl bootstrap workers [dhcp-ip...]
Provision multiple worker nodes in parallel
--clusterCluster name (reads worker.yaml from config/<cluster>/)--concurrencyMax workers to provision (reboot + image preload) at once — higher values reboot more nodes simultaneously, which increases DHCP/ARP/switch contention and the odds of a transient preload failure on some node--siteRead worker nodes from site.yaml instead of scanning config/*.json
rctl bootstrap controlplanes
Bootstrap all control-plane nodes from site.yaml
--sitePath to site.yaml (required)
rctl bootstrap etcd
Bootstrap all etcd nodes from site.yaml
--sitePath to site.yaml (required)
rctl bootstrap etcd-init
Initialize the first node of a dedicated external etcd cluster
--clusterCluster name (etcd CA saved to config/<cluster>/pki/etcd/)
rctl bootstrap etcd-join
Join a node to an existing dedicated external etcd cluster
--clusterCluster name--etcd1Static IP of an existing etcd node (auto-detected from etcd-members.json if omitted)
rctl bootstrap addons
Finish the imperative addons once the nodes have applied Cilium (CoreDNS, discovery, CA)
--caAD CS CA for intermediate cert enrollment (e.g. "host\CA-name"). If set, runs 'cert intermediate' + applies issuer after cert-manager is ready.--clusterCluster name (resolves config/<cluster>/ for admin.conf)--config-dirDirectory containing admin.conf (overrides --cluster if set)--github-app-idGitHub App ID for ArgoCD repo access (optional; if provided with --github-app-installation-id and --github-app-key, creates ArgoCD repo secret)--github-app-installation-idGitHub App Installation ID--github-app-keyPath to GitHub App private key PEM file--gitops-repoGitOps repo URL (required; set gitops.repo_url in site.yaml)--registry-hostRegistry host(s) to configure containerd trust for (e.g. registry.owc.cobritek.com)--root-ca-filePath to AD root CA PEM file. When provided, pushes root CA to nodes instead of intermediate — enables full chain verification for all enterprise certs.--skip-node-trustSkip pushing the CA into ruckos node trust
rctl bootstrap storage
Install storage CSI drivers (local-path, NFS)
--clusterCluster name (resolves config/<cluster>/ for admin.conf)--config-dirDirectory containing admin.conf (overrides --cluster)--local-deviceNode-local block device to mount for local-path--local-formatFormat --local-device before mounting if it is blank--local-fstypeFilesystem type for --local-device--local-pathNode-local host path for local-path-provisioner--nfs-serverNFS server IP or hostname--nfs-shareNFS export path from the storage server (e.g. /k8s for a Windows share named k8s)--skip-localSkip local-path-provisioner--skip-nfsSkip csi-driver-nfs--skip-preflightSkip storage node preflight checks
rctl bootstrap platform
Install the full platform stack from site.yaml (addons → ArgoCD → app-of-apps)
--sitePath to site.yaml (required)--skip-addonsSkip addons step (wait for node-applied Cilium, CoreDNS, CA)--skip-app-of-appsSkip app-of-apps step--skip-argocdSkip ArgoCD install step--skip-storageSkip storage step
rctl bootstrap lighthouse
Deploy Lighthouse + Postgres onto a bootstrapped cluster (no platform dependency)
--sitePath to the site file (required)
rctl bootstrap push-ca
Push the enrolled intermediate CA to all node trust stores
--clusterCluster name (resolves config/<cluster>/ for admin.conf and rctlconfig)--config-dirDirectory containing admin.conf and rctlconfig (overrides --cluster)--registry-hostRegistry host(s) that should use this CA for containerd pulls--root-ca-filePath to AD root CA PEM file. Preferred over --secret: nodes trust the root, enabling full chain verification for all enterprise certs.--secretKubernetes secret name containing the CA cert (tls.crt key)--secret-namespaceNamespace of the CA secret--sitePath to site.yaml — if provided, also push CA to non-K8s nodes listed there (e.g. external etcd nodes)
rctl bootstrap push-cp
Low-level: push control-plane PKI and manifests to a node (use 'bootstrap init' for end-to-end provisioning)
--cluster-nameKubernetes cluster name--hostnameControl plane node hostname (auto-detected from node config if omitted)--l2-ifaceCilium L2 announcement interface (site networking.l2_announce_iface)--lb-endCilium LB pool end IP (site networking.lb_pool.end)--lb-startCilium LB pool start IP (site networking.lb_pool.start)--out-dirDirectory to save admin kubeconfig and CA certs locally--pod-cidrPod CIDR (for controller-manager)--service-cidrService CIDR
rctl bootstrap push-worker
Low-level: push worker PKI files to a node (use 'bootstrap worker' for end-to-end provisioning)
--cluster-nameKubernetes cluster name--cpControl plane node IP address--out-dirDirectory containing cluster CA certs (default: <project-root>/config)--pki-dirPath to cluster PKI directory (default: --out-dir/pki)--worker-nameWorker node hostname (auto-detected from node config if omitted)
rctl bootstrap push-files
Push a directory of pre-generated node files to a node
--node-dirDirectory of files to push (output of 'rctl gen config', node/ subdir)
rctl bootstrap etcd-join-remove
Remove a stale etcd member left by a failed 'bootstrap etcd-join'
--clusterCluster name--etcd1Static IP of an existing etcd node (auto-detected from etcd-members.json if omitted)--removeHostname of the etcd member to remove (required)
rctl bootstrap join-remove-member
Remove a stale etcd member left by a failed 'bootstrap join'
--clusterCluster name--cp1IP of the existing control-plane node (required)--removeHostname of the etcd member to remove (required)
Hypervisors and VMs
rctl bootstrap hypervisor <node-ip> [--wipe] [--cluster <name> [--join <host-ip>]]
Turn a maintenance node into a hypervisor that runs VMs, or add a VM store
--clusterPut the hypervisor in this cluster: a new one, or with --join an existing one--joinIP of a host already in the --cluster cluster--wipeAlso erase the chosen disk or partition when it is already a VM store (asks first)
rctl vm create <host-ip> --name <name> --cpus <n> --memory <size> --disk <size> [--store <store>] [--iso <file>] [--autostart]
Create a VM and start it
--autostartStart the VM whenever the host boots--cpusNumber of virtual CPUs--data-diskAdd an empty data disk (sdb in the guest), e.g. 10G--diskDisk size, e.g. 40G or 1T--disk-typedynamic or fixed, for the OS disk and the data disk--isoInstall ISO on this machine to upload and attach--memoryMemory, e.g. 4G or 4096M--nameVM name (lowercase letters, digits, dashes)--storeVM store (disk) on the host; needed when it has more than one
rctl vm list <host-ip>
List the VMs on a hypervisor node
rctl vm start <host-ip> <name>
Start a VM
rctl vm stop <host-ip> <name> [--force]
Shut a VM down (--force turns it off immediately)
--forceTurn the VM off immediately
rctl vm set <host-ip> <name> [--cpus <n>] [--memory <size>]
Change a stopped VM's CPUs and/or memory
--cpusNumber of virtual CPUs--memoryMemory, e.g. 8G or 8192M
rctl vm move <host-ip> <name> --to <hostname> [--live]
Move a VM to another host of its Ruckos cluster
--liveKeep the VM running while it moves (running VM only)--toHostname of the host to move the VM to
rctl vm delete <host-ip> <name> [--yes]
Delete a stopped VM and its disk
--yesSkip confirmation prompt
rctl vm disk add <host-ip> <name> --size <size> [--disk-type dynamic|fixed]
Add an empty data disk to a stopped VM
--disk-typedynamic or fixed--sizeData disk size, e.g. 20G
rctl vm disk remove <host-ip> <name> --disk <n> [--yes]
Remove a stopped VM's data disk and delete its contents
--diskData disk number (1 = first data disk)--yesSkip confirmation prompt
rctl cluster status <host-ip>
Show the hypervisor cluster a host is in: its hosts, alive or lost, and which run etcd
rctl cluster cpu <host-ip> [model]
Show or set the CPU model VMs of the hypervisor cluster start with
rctl cluster leave <host-ip>
Take the live host at <host-ip> out of its hypervisor cluster
rctl cluster remove <host-ip> <hostname>
Take a lost host out of the hypervisor cluster that the host at <host-ip> is in
rctl disk list <node-ip>
List a node's disks, partitions and free space
rctl disk delete <node-ip> <partition>
Delete a partition so its space is free again
rctl disk remove-store <node-ip> <store>
Take a VM store with no VMs out of use so its partition can be deleted or reused
Certificates
rctl cert request <secret-name>
Request a cert from AD CS and store it as a Kubernetes TLS Secret
--caAD CS CA config string: "host\CA-name"--dnsDNS SANs for the certificate (repeatable, or comma-separated)--kubeconfigPath to kubeconfig (defaults to KUBECONFIG env or ~/.kube/config)--namespaceKubernetes namespace for the TLS Secret--templateAD CS certificate template name
rctl cert intermediate
Enroll one intermediate CA from AD CS for cert-manager's `ca` issuer
--caAD CS CA config string: "host\CA-name"--cnCommon Name (subject) for the intermediate CA--from-cerAssemble the manifest from an already-issued .cer (phase 2, after approval)--issuer-nameName for the generated ca ClusterIssuer--keyPrivate key path (default: <output-dir>/pki/intermediate.key)--manifestManifest output path (default: <output-dir>/adcs-intermediate-issuer.yaml)--namespaceNamespace for the intermediate Secret--output-dirDirectory for the saved key and generated manifest--secret-nameName for the intermediate tls Secret--templateAD CS subordinate-CA certificate template name
Clusters and node operations
rctl get-clusters
List all known clusters and their live status
rctl get-nodes
List nodes across all clusters (or a specific one)
--clusterFilter to a specific cluster (default: all clusters)
rctl nodes get
List cluster nodes with roles and cluster label
--clusterCluster name (reads config/<cluster>/admin.conf)
rctl nodes health <ip1> <ip2> ...
Check health of multiple nodes in parallel
rctl etcd status
Show each etcd member and its health
--clusterCluster name (reads config/<cluster>/)
rctl kubeconfig [dir]
Write the admin kubeconfig for the cluster
--fromControl plane machine config to extract the kubeconfig from
rctl cluster-info
Show cluster identity information from a node
rctl health
Check node health
rctl version
Show node OS and component versions
rctl logs <service>
Tail logs for a node service (kubelet, containerd, api)
rctl dmesg
Show kernel ring buffer (dmesg) from node
rctl exec -- <command> [args...]
Execute a command on the node
rctl reboot
Gracefully reboot a node
--yesSkip confirmation prompt
rctl shutdown
Gracefully power off a node
--yesSkip confirmation prompt
rctl render
Render Kubernetes manifests from a deployment descriptor
--domainPlatform DNS domain--environmentTarget environment (dev|qa|prod); overrides the descriptor. Set by CI from the branch.--filePath to the deployment descriptor--gitops-rootGitOps repo root. When set, writes to <root>/apps/<env>/<appname>/<name>/ and namespaces by appname (overrides --out).--image-tagImage tag to inject; replaces the tag in the descriptor image. Set by CI from the commit SHA.--namespaceTarget namespace (default: descriptor namespace or name)--outDirectory to write rendered manifests--pull-registryRegistry host used in rendered manifests for pod image pulls. Nodes pull via this hostname (AD DNS -> registry LoadBalancer .84).--pull-secretimagePullSecret name baked into workloads