Docs / Kubernetes

Kubernetes beta

Kubernetes (k3s) clusters on servers you own, installed and maintained by Saka. No control plane fee, no cloud load balancer, and no YAML for ordinary apps.

Who it is for

Teams already using containers and Kubernetes, or starting to run many services on many servers. For a single site or app, Sites and Apps from Git are simpler.

Create a cluster

  1. Prepare empty servers (no Saka sites), Ubuntu 24.04, at least 2 GB RAM, preferably in one region. Connect them to Saka like any server.
  2. Open the Kubernetes menu, press Create cluster, name it, and pick the servers.
  3. Roles are automatic: 1-2 servers = 1 control plane; 3 or more = 3 control planes (the cluster keeps running when one dies) and the rest workers.

Saka installs k3s, joins the nodes over the private network when the servers share a subnet (otherwise over encrypted WireGuard on public IPs), then installs the default components. A 3-node cluster is ready in about 2 minutes; you are notified when it is done.

The join token is read from the first node and passed straight to the others without being stored by Saka. No kubeconfig is stored either; commands run through the agent on a control plane.

Default components & settings

Test on IDCloudHost, 6 nodes on a private network: the control plane holding etcd and Traefik was powered off; 1% of requests failed over 14 seconds, the Kubernetes API kept answering, and everything was complete again within 76 seconds.

Adopt an existing cluster

For a k3s cluster you installed yourself: install the Saka agent on every cluster server, press Adopt cluster, and pick one control plane. Other nodes are recognized by IP or hostname. Saka adds Traefik on every node and cert-manager; your workloads are not changed.

Deploy an app

Press Deploy app and fill in the container image (e.g. nginx:1.27 or ghcr.io/name/app:v1), port, replicas, RAM limit, domain, and variables. Saka builds the Deployment (replicas spread across nodes), Service, and Ingress with HTTPS. Point the domain's A record to the public IP of any node; the Let's Encrypt certificate is issued by itself, in 42 seconds in our test. Deploy again with the same name to update the image or replica count.

For anything else (Secrets, ConfigMaps, StatefulSets, CronJobs, Helm charts via the k3s HelmChart), use Apply YAML, the same as kubectl apply.

Monitor & manage

The cluster page has five tabs: Overview (layout advice, failing pods, and a summary of nodes Ready, apps, domains and the latest snapshot), Apps, Nodes, Data & backups and Access. The active tab is kept in the page address, so you can share it. Running operations and errors always show above the tabs.

Upgrading k3s

When a new k3s version is out, the cluster page shows an Upgrade available button. Only the latest patch of the same minor (security fixes) or the next minor are offered; Kubernetes must not skip minors. The oldest node is the reference, so an upgrade that stopped halfway can be resumed.

  1. An etcd snapshot is taken first. If that fails, no node is touched.
  2. One node at a time, control planes first, then workers: cordon and drain (pods move to other nodes), the k3s binary is replaced with the official GitHub release verified by SHA256, k3s restarts, then Saka waits until the node is Ready on the new version and it accepts pods again.
  3. If a node fails (download error, not Ready within 5 minutes), it is rolled back to its previous binary, uncordoned, and the upgrade stops with an explanation. Nodes that are already upgraded are skipped when you run it again.

A single-node cluster stops briefly (usually under a minute) while k3s restarts. For a new minor, check the Kubernetes release notes if your apps or charts use old beta APIs.

Test on 9 Oct 2026 (control plane + worker, an app with 2 replicas stayed 2/2 throughout): v1.35.8 to v1.36.5 in 78 seconds, then to v1.37.1. A deliberately blocked download on the worker stopped the upgrade safely; once unblocked, it resumed and skipped the already upgraded control plane.

Node-failure-proof volumes (Longhorn)

k3s's default, local-path, keeps volume data on one node. If that node dies, pods using it are stuck until the node is back or the data is restored from backup. For databases and uploads on clusters with 2 or more nodes, turn on Node-failure-proof volumes in the Volume storage section:

Test on 9 Oct 2026 (3 nodes): the server running a database pod was powered off. The pod ran again on another node and the app resumed writing 104 seconds after its last write before the power-off, with data intact. When the server was powered back on, the third copy was rebuilt and the volume was healthy again within about 2.5 minutes.

App domains keep working when a node dies

Traefik runs on every node, so any node can serve every app. The problem is a domain pointed at one node's IP: if that node dies, the site goes down even though the app has moved. So every cluster has an ingress address app-<id>.k8s.saka.work with an A record for every node that is Ready, connected and actually answering on port 80 (checked every minute). A dead node is removed from DNS within about a minute and added back when it recovers.

The Addresses section of the cluster page checks every domain's DNS through a public resolver: CNAME to the cluster, A to all nodes, only some nodes, an IP outside the cluster (e.g. a proxy), or not pointing yet. Traefik is also tuned so requests to a pod on a dead node are retried on another pod quickly (3 seconds, not 30).

Test on 9 Oct 2026 (3 nodes, app with 2 replicas, domain as a CNAME to the ingress address): a node running one of the pods was powered off while curl ran every 2 seconds from outside; 127 of 128 requests succeeded. One failed because the client itself tried the dead node's IP before DNS was updated, a normal limit of DNS-based failover.

Telegram alerts

Saka checks every cluster each minute and notifies you on Telegram (or email if Telegram is not linked), then again when it recovers:

Alerts are held while the cluster is adding or removing a node or being restored, since a brief NotReady is expected then. The check is light (three kubectl calls on one control plane). All alerts also show on the panel's Alerts page.

Backups are per cluster, not per VM

In Kubernetes, the cluster is what gets backed up, not each VM. That is why the per-VM site backup card is hidden on server pages that are nodes.

App volume backups (Velero)

Optional, one button in the backups section: Turn on volume backups. Saka installs Velero (open source) with the S3 plugin and a small agent on every node, pointed at the cluster's S3 destination. Resources and volume contents of all app namespaces are backed up daily at 02:00, plus a Back up volumes now button; retention follows the S3 setting. About 150 MB of extra RAM, plus the agent on each node.

Test on 9 Oct 2026: an app with data in a volume was backed up (38 items), its namespace was deleted entirely, then restored; the data came back intact. The repository password computed from the recovery code matched the one used in the cluster.

Test on 9 Oct 2026: snapshots in S3 were confirmed encrypted, and with the recovery code they opened back into an intact etcd database.

Restore a cluster from a snapshot

The Restore cluster button in the Backups section returns the whole cluster state (deployments, services, secrets, ingresses and other resources in etcd) to when the snapshot was taken. Pick a local or S3 snapshot per control plane, then type the cluster name. The steps:

  1. A snapshot of the current state is saved first as sebelum-pulih, so a restore can be undone by restoring that snapshot.
  2. All control planes are stopped and etcd is reset from the chosen snapshot (an S3 snapshot is downloaded and decrypted with the key on the node).
  3. The other control planes set their old etcd data aside (not deleted, in db.sebelum-pulih-*) and join again.

The Kubernetes API is down briefly; running apps usually keep serving. All control planes must be connected to Saka. Volume contents are not included; restore them with Velero if needed.

Test on 9 Oct 2026 (2 control planes): restoring from an S3 snapshot took 47 seconds and from a local snapshot 42 seconds; resources deleted after the snapshot came back, resources created after it were gone, both nodes Ready.

Add & remove nodes

A cluster can grow at any time. Install the Saka agent on the new server (a VM from any provider or a bare-metal machine, Ubuntu 22.04/24.04, at least 2 GB RAM, no Saka sites), then press Add node on the cluster page and pick a role:

Saka uses the same k3s version and network mode as the cluster. A cluster whose nodes share one private network (vxlan) only accepts servers on that same private network; servers at another provider or location are refused with an explanation, because node traffic has to travel over that private network. A cross-network cluster (WireGuard) accepts any server with a public IP. The server hostname must be unique in the cluster because it becomes the node name.

Bare metal: install Ubuntu Server, then run the agent install command from the Servers menu just like a VM. Once connected, the machine shows up in the Add node list.

Remove on a node row: pods are moved to other nodes first (drain), the node is deleted from the cluster, then k3s is uninstalled from the server. local-path volumes on that node are lost, so make sure volume backups are on. The server stays in Saka as a regular server. The last control plane cannot be removed.

Add capacity in one click

In the Nodes tab (or from the Capacity card in Overview when it is almost full), press + Add capacity and choose a cloud account, size and count (1-3). Saka creates new servers in your cloud account, automatically on the cluster's private network, waits for them to boot and joins them as workers. It takes about 5-10 minutes and you are notified; servers are billed by your cloud provider. Needs a connected cloud account (Settings, Cloud accounts). AI agents use the MCP tool k8s_tambah_kapasitas.

The Capacity card in Overview shows total, used and free CPU, RAM and disk, what apps have reserved, and the space still available for volumes.

Node labels & taints

When adding a node, or with the Labels button on a node row, you can set labels (type=gpu, zone=jakarta) so apps can pick nodes with nodeSelector/affinity, and taints (dedicated=gpu:NoSchedule) so only pods with a matching toleration are scheduled there. Taints on a new node apply from registration, so no other pod slips in first. Kubernetes/k3s labels and taints (kubernetes.io, k3s.io) are protected.

In the Deploy app dialog, the Run on choice lists your node labels. Picking one sets a nodeSelector, and taints on those nodes are tolerated automatically, so dedicated nodes (e.g. GPU) can be used without writing YAML.

When to add a control plane, when to add a worker

Rule of thumb: control planes are for resilience, workers are for capacity. The Cluster layout advice card on the cluster page reads the cluster state and tells you the next step, with a button that picks the right role:

Node summary on the server page

The page of a server that is a node shows the node status (Ready, memory/disk pressure), CPU/RAM usage, labels & taints, and the pods on that node with their state, restarts and usage, like kubectl describe node and kubectl get pods --field-selector spec.nodeName=....

Reboot a node without stopping apps

The Reboot button on the server page (and the Fix button for a pending reboot after system updates) does not simply power-cycle a Kubernetes node. Saka first stops new pods from landing on it and moves its pods to other nodes (cordon & drain), reboots the server, waits until it has actually gone down, come back and is Ready again, and then lets it accept pods again. If pods cannot be moved, the reboot is cancelled and the node keeps running. Rebooting one node of a 6-node cluster took about 1.5 minutes with no app downtime.

kubectl access (kubeconfig)

The kubectl access section creates kubeconfigs for kubectl, Lens, CI or AI agents. Each access is its own ServiceAccount with a token that expires on its own (1 to 365 days) and can be revoked any time; a revoked token is rejected immediately. Saka does not store the token; the kubeconfig is shown only once.

Fixed API address. Kubeconfigs point at <id>.k8s.saka.work, not one server's IP. That name has an A record for every healthy control plane; kubectl tries the addresses in turn, and Saka removes a dead control plane from DNS within about a minute and adds it back when it recovers. New clusters use it right away; for older clusters press Turn on fixed API address (control planes restart one at a time; apps are not affected), then create the kubeconfig again.

Test on 9 Oct 2026 (3 control planes): one control plane was powered off while kubectl ran every 3 seconds from outside the cluster; 100 of 100 commands succeeded (two took about 10 seconds while switching address), DNS was updated within about 18 seconds and was complete again once the server was back.

Do not hand /etc/rancher/k3s/k3s.yaml to other people or AI agents: it is an admin certificate that cannot be revoked. kubectl connects to port 6443 on the control plane, so that port must be reachable from your computer.

Helm charts

The Helm charts section installs apps from a Helm repo (https) or an OCI registry through k3s's built-in Helm controller, with no helm on your computer. Fill in the repo, chart, version (optional), release name, namespace and values like values.yaml. The same release name means upgrade. If it fails (missing chart, bad values), the Helm log is shown. Uninstalling runs helm uninstall; the namespace and PersistentVolumeClaims are not deleted.

Rebuild when all nodes are gone

If every server of a cluster is gone (deleted, broken, provider outage), the cluster can be rebuilt on new servers with just the S3 recovery code. On the Kubernetes page press Restore from backup, select new empty servers, paste the recovery code, then Find backups. The new server reads the bucket; Saka never sees the backup contents. There are two methods:

Test on 9 Oct 2026: the source cluster's server was deleted, then rebuilt twice. Through Velero it took 3.5 minutes and the app's volume data came back identical; through etcd it took 34 seconds with the same CA and IP ranges.

10.42.x / 10.43.x networks

k3s uses 10.42.0.0/16 for pods and 10.43.0.0/16 for Services. If a server's private IP is in those ranges (e.g. IDCloudHost's default 10.43.x network), Saka automatically uses 10.52.0.0/16 and 10.53.0.0/16 so Service IPs never collide with server IPs. The choice carries over to every new control plane and to restores.

Nodes are not empty VMs

Servers that are Kubernetes nodes are marked in the server list. VM-style site creation, app installs, firewall, swap, and moving web ports are hidden and refused, since they would fight the ingress for ports 80/443 or close ports between nodes. Monitoring, Server security, Shield, and system updates keep working.

Through AI agents

MCP tools: k8s_daftar, k8s_status, k8s_buat, k8s_adopsi, k8s_aplikasi, k8s_terapkan, k8s_hapus, k8s_log, k8s_cadangan, k8s_node, k8s_pulihkan, k8s_cari_cadangan, k8s_upgrade, k8s_penyimpanan, k8s_domain, k8s_akses, k8s_helm, k8s_volume, and k8s_temuan. Actions that change the cluster need your approval.

Release from Saka

Release cluster from Saka only stops Saka from managing it; k3s and all apps keep running, and the cluster can be adopted again any time.

Coming soon

Restoring a cluster from an etcd snapshot in the panel, rolling version upgrades, adding or removing nodes from the panel, a Saka ISO for dedicated servers, and node monitoring in Telegram alerts.