Your cloud · Kubernetes
A Kubernetes cluster, explained before it is built.
Kubernetes is a coordinator. It helps several machines run the right container in the right place and replace it when it fails. Saphira makes the plan visible first, so a cluster is something you choose—not something that happens to you.

The short version
Four small ideas are enough to begin.
A control plane
The organiserA worker
The runnerA Pod network
The private roadsA service network
The signpostsA single machine can be useful for learning or a small internal service. Several machines are useful when there is a real reason to spread work or tolerate a machine being offline. If one carefully managed Docker host meets the need, that is often the simpler answer.
What Saphira gives you
The packages provide the parts; the helper provides a careful way to join them.
kubernetes supplies kubelet, kubeadm and kube-proxy. containerd runs containers and cni-plugins supplies the networking building blocks.saphira-kubernetes is the Saphira helper that reads your declared plan, checks authorised machines, prints the intended steps, applies them only with explicit confirmation and verifies the result.
Installing saphira-kubernetes starts no service, creates no cluster and authorises no remote machine. It installs a deliberately empty configuration skeleton and the two documents below. This is the point: the network and the machines remain your decision.
apk update
apk add containerd cni-plugins kubectl kubernetes saphira-kubernetes
# Read the contract before editing any cluster configuration.
sed -n '1,240p' /usr/share/doc/saphira-kubernetes/CONFIG.md
sed -n '1,160p' /usr/share/doc/saphira-kubernetes/example-cluster.confSaphira or SaphiraD: the same cluster, the right service command
Every relevant APK carries both service definitions. On Saphira, containerd and kubelet are OpenRC scripts in /etc/init.d/. On SaphiraD, the same names are systemd units in /usr/lib/systemd/system/. The container runtime is needed before the node agent; kubelet then receives its detailed configuration from kubeadm during cluster creation or joining.
Saphira · OpenRC
rc-update add containerd default
rc-service containerd start
# Enable kubelet once this node is being created or joined.
rc-update add kubelet default
rc-service kubelet startSaphiraD · systemd
systemctl enable --now containerd.service
# Enable kubelet once this node is being created or joined.
systemctl enable --now kubelet.serviceDo not be alarmed if kubelet has nothing useful to do before a node is initialised or joined: it is designed to start before its final kubeconfig exists so it can complete its own TLS bootstrap. A stray start is not a cluster. The proof remains the helper’s verify command after the cluster plan has been applied.
Read these two files like a map and a legend
example-cluster.conf is the map: a small, concrete example of the choices a cluster needs. CONFIG.md is the legend: it explains every file, every setting and the safety rules behind them. The example is not copied blindly; its addresses are documentation values and its name is only a name.
hosts
/etc/saphira-kubernetes/hostsworker1 root@worker1.example. A name absent from this file is refused. Add and verify SSH host keys yourself; the helper never accepts a new key behind your back.clusters/production.conf
One clusterclusters/production.d/cp.conf
One machinehosts, and—on a control plane—the address it advertises to joining machines.Assigned Pod ranges
Written after joiningA small, deliberate cluster plan
Use your own names and networks; these values only show the shape of the files.
# /etc/saphira-kubernetes/clusters/production.conf
CLUSTER_NAME=production
MODE=dual
K8S_VERSION=1.37.1
POD_SUBNET_V4=10.115.0.0/16
POD_SUBNET_V6=2a02:8012:bc57:30::/60
SERVICE_SUBNET_V4=10.96.0.0/12
SERVICE_SUBNET_V6=2a02:8012:bc57:40::/112
KUBE_PROXY_MODE=nftables
SINGLE_NODE_WORKLOADS=no
# /etc/saphira-kubernetes/clusters/production.d/cp.conf
NODE_ROLE=control-plane
NODE_HOST=local
NODE_ADVERTISE=192.0.2.10
# /etc/saphira-kubernetes/clusters/production.d/worker1.conf
NODE_ROLE=worker
NODE_HOST=worker1MODE=dual means this example has both IPv4 and IPv6. Choose ipv4 or ipv6 instead when that is all the environment uses; the matching family’s Pod and Service range is then required. The ranges are not public internet addresses. They are the cluster’s private internal space, so choose ranges that do not overlap with the office, home, VPN or another cluster.
A worker needs the name of its control-plane member at apply time. That lets the helper mint a fresh two-hour join token only when it is needed. The token is not saved in the configuration or copied into a document.
Before pressing apply
This is where the pain normally lives. Saphira makes it a checklist.
Each member needs forwarding enabled, swap turned off, the overlay and br_netfilter kernel facilities available, the bridge netfilter sysctls set, CNI plugins under /opt/cni/bin, and containerd configured with SystemdCgroup=true. These are not decorative details: without them, containers may start but fail to reach one another reliably.
Start with inventory and the plan. They do not alter machines. Read their output, fix missing prerequisites and make sure the machine names and ranges are exactly yours.
saphira-kubernetes inventory --host local --host worker1
saphira-kubernetes plan --cluster production
# Print the changes first; no files or services are changed in dry-run mode.
saphira-kubernetes --dry-run --confirm apply --cluster production --control-plane cp
# Make the same declared plan real only when it reads correctly.
saphira-kubernetes --confirm apply --cluster production --control-plane cpApply is idempotent: repeating a converged plan is designed to be a no-op. It does not make a high-availability promise, choose storage for an application or turn an unfinished network plan into a production system. Those remain useful, visible decisions.
Optional tools: choose the job, not the jargon
A cluster can run without any of these. Add one only when it solves a job you actually have.
Helm
Install a prepared applicationcrictl
Look underneath Kubernetesk9s
A terminal dashboardHeadlamp
A private web viewThe FEATURES line in the cluster file is a shopping list for these optional helpers. It is not a workload list. The cluster still contains only the workloads that an administrator deliberately deploys afterwards.
# In /etc/saphira-kubernetes/clusters/production.conf
FEATURES=helm crictl k9s headlamp
# Headlamp's read-only dashboard files are a separate package on the machine
# where you run saphira-kubernetes.
apk add headlamp
# See what the helper intends to add, then make only that declared plan real.
saphira-kubernetes plan --cluster production
saphira-kubernetes --confirm apply --cluster production --control-plane cp
saphira-kubernetes features --cluster productionYou can list only the tools that are useful; for example FEATURES=crictl is a sensible choice for node diagnosis, while a small cluster with no application charts may have no reason to install Helm. The helper refuses unknown names rather than guessing.
Use each optional tool safely
Helm: inspect before installing
Start by reading the publisher’s chart instructions and the values it accepts. A chart can create accounts, network services, storage requests and several running containers. Treat it as application configuration, not a magic button.
helm version
helm list --all-namespacescrictl: diagnose one node
Use it on the affected machine when you need to see what containerd sees. The packaged configuration points it at the local containerd socket.
crictl ps -a
crictl imagesk9s: browse with your existing access
Run it on a control-plane machine, or anywhere with an appropriate kubeconfig. It is a view and navigation aid; stop and understand an action before confirming it.
k9sHeadlamp: open it privately
Create a login token, then make a private tunnel from your own browser to the internal service. Visit http://127.0.0.1:8080 only while the tunnel is running and use that token to sign in.
kubectl -n headlamp create token headlamp-admin
kubectl -n headlamp port-forward service/headlamp 8080:80Do not change Headlamp into a public NodePort or load balancer merely for convenience. Its Saphira package pins the dashboard image, places its objects in their own headlamp namespace and binds it to Kubernetes’ standard read-only view role. Removing the feature deletes only that namespace; it does not delete other workloads, their images or their storage.
Know when it worked
A green package installation is not the finish line.
saphira-kubernetes verify --cluster production
saphira-kubernetes verify --cluster production --deepVerification checks that the API is reachable, every node reports Ready and Kubernetes system Pods are running. The --deep check also starts a short-lived DNS probe, then removes it. A clean result ends with cluster production verifies clean.
If a worker did not join, first check the authorised hosts entry, the control-plane advertise address, SSH access, the version line and the prerequisite checks. Do not keep retrying with guessed commands. The plan and inventory give a stable place to compare what was intended with what is present.
When retiring a node, use the helper’s drain, remove-node or remove-cluster actions with --confirm. They name what will be removed first. In particular, drain warns about bare Pods, because those have no controller to recreate them elsewhere.