Skip to content

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.

Saphira Linux dragon mascot

The short version

Four small ideas are enough to begin.

A control plane

The organiser
The machine that keeps the list of work, decides where it should run and issues the credentials other machines need.

A worker

The runner
A machine that joins the cluster and runs the application containers assigned to it.

A Pod network

The private roads
An address range reserved for the containers. It must be planned so it does not collide with the networks already in use.

A service network

The signposts
A separate internal address range Kubernetes uses to give applications stable names and destinations.

A 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.conf

Saphira 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 start

SaphiraD · systemd

systemctl enable --now containerd.service

# Enable kubelet once this node is being created or joined.
systemctl enable --now kubelet.service

Do 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/hosts
This is the guest list. Each line is a friendly machine name, a tab, and its SSH destination—for example worker1 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 cluster
This describes the shared decisions: the cluster name, IPv4, IPv6 or both, Kubernetes version, Pod range, Service range, kube-proxy mode and any optional management tools.

clusters/production.d/cp.conf

One machine
This says whether a named machine is the first control plane or a worker, where it is found in hosts, and—on a control plane—the address it advertises to joining machines.

Assigned Pod ranges

Written after joining
Do not invent a per-node PodCIDR. Kubernetes assigns it. The helper records the assigned range and gateway in that node’s file, then uses it when it renders the local CNI configuration on a later apply.

A 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=worker1

MODE=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 cp

Apply 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 application
Helm is a package manager for Kubernetes applications. Think of it as a carefully named set of installation instructions for an application that has several Kubernetes pieces. Use it when an application publisher provides a Helm chart and you have read what that chart will create. It is a client program, not a background service, and it does not install applications by itself.

crictl

Look underneath Kubernetes
crictl is a mechanic’s inspection tool. It talks directly to the local containerd runtime on one machine and can show containers and images when Kubernetes is not giving enough detail. Use it for diagnosis on a node; it is not the normal way to deploy or manage a whole cluster.

k9s

A terminal dashboard
k9s is a full-screen terminal view of a cluster. It makes Pods, logs, events and common actions easier to browse than a long sequence of commands. It uses the same permissions as the kubeconfig of the person running it; it does not create a web site or grant extra access.

Headlamp

A private web view
Headlamp is an optional browser dashboard. Saphira deploys it with read-only cluster permissions and keeps it inside the cluster by default. “ClusterIP” simply means it has an internal address: it is not published directly to the internet or every machine on the network.

The 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 production

You 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-namespaces

crictl: 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 images

k9s: 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.

k9s

Headlamp: 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:80

Do 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 --deep

Verification 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.