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
- 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.
- Open the Kubernetes menu, press Create cluster, name it, and pick the servers.
- 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
- Traefik on every node (DaemonSet). k3s ships a single copy; in our tests, losing the node that held it failed 11% of requests.
- cert-manager with a Let's Encrypt issuer for automatic HTTPS.
- Dead nodes are detected in 20 seconds and pods are moved 30 seconds later (Kubernetes defaults to about 5 minutes). App ingresses retry on another replica.
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.
- Nodes: role, private and public IP, Ready or not.
- Apps & workloads: ready/desired replicas, images, logs, delete. System components (kube-system, cert-manager) are hidden and protected.
- Failing pods with their reason, e.g. ImagePullBackOff or CrashLoopBackOff, with a logs button.
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.
- An etcd snapshot is taken first. If that fails, no node is touched.
- 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.
- 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:
- Saka installs open-iscsi on every node (including nodes added later), then Longhorn 1.12. Every volume is copied to 3 nodes (2 if the cluster has only 2 nodes).
- When a node dies, its pods are moved to another node automatically and the volume follows, without waiting for the node to return. When the node comes back, missing copies are rebuilt automatically.
- It can be the default class for new PVCs (Saka also stops k3s from restoring local-path as the default). Existing volumes are not moved automatically.
- Every volume is listed with its health. Volumes missing copies for more than 30 minutes, or failed, are reported on Telegram.
- Uses about 300-500 MB RAM per node; 4 GB nodes are recommended.
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.
- Subdomains (shop.example.com): create a CNAME to that address. Most resilient, as it follows the nodes automatically.
- Root domain (example.com, cannot be a CNAME): create A records for all IPs shown.
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:
- Node NotReady for more than 90 seconds (server down, node network broken, k3s stopped). If that server is also disconnected from Saka, only the server-disconnected alert is sent.
- Node out of resources (MemoryPressure, DiskPressure, PIDPressure) for more than 2 minutes.
- Pods stuck because CPU/RAM is fully reserved for more than 5 minutes, with advice to add a worker.
- App pods failing (CrashLoopBackOff, OOMKilled, ImagePullBackOff) for more than 5 minutes.
- HTTPS certificates not issued after 15 minutes (usually DNS not pointing at the cluster yet).
- etcd snapshots not taken for more than a day.
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.
- etcd snapshots hold every cluster resource (Deployments, Services, Ingresses, Secrets, and more). Taken automatically every 12 hours, the last 5 kept on each control plane, plus a Back up now button.
- To your own S3, encrypted. On the cluster page, set an S3 destination once (Amazon S3, Cloudflare R2, DigitalOcean Spaces, Wasabi, Backblaze B2, or any S3). Saka installs it on every control plane with the same encryption key, and each control plane uploads its snapshots encrypted (age) to
saka/k8s/<cluster>/<node>/. Snapshots survive even if all nodes are lost. S3 and encryption keys are stored only on the nodes, never by Saka. - Recovery code, the same as for site backups: keep it safe, since snapshots cannot be opened without it.
- App data in volumes (databases inside pods, uploads) is not part of etcd snapshots; volume backups below cover it.
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.
- Encrypted with your recovery code. Velero encrypts volume contents with a repository password that is the same on every default install. Saka derives that password from the key in the recovery code instead, so the same code is enough to open volume backups even if the whole cluster is gone.
- Restore from the backup list. Resources that still exist are not overwritten: to fully restore a broken app, delete its namespace or app first.
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:
- A snapshot of the current state is saved first as
sebelum-pulih, so a restore can be undone by restoring that snapshot. - 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).
- 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:
- Worker (default): runs your apps. Add workers when the cluster runs low on RAM or CPU.
- Control plane: also stores cluster data (etcd). Odd counts are safest: 3 survives one failure, 5 survives two.
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:
- 1 control plane: the cluster cannot be managed if that server dies. Add 2 control planes (3 total).
- Even number of control planes (2, 4): etcd needs a majority, so 2 is no more resilient than 1. Add 1 to make it odd.
- More than 5 control planes: no extra resilience; add workers.
- Pods waiting because CPU/RAM is fully reserved, reserved CPU/RAM above 70%, or average real RAM above 80%: add a worker.
- A control plane that also runs apps and is nearly out of RAM: add a worker so etcd is not starved.
- Pods with no node because taints/labels do not match: check labels & taints; adding a regular node will not help.
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.
- Viewer: read resources and nodes, no Secrets. Good for AI agents that analyze the cluster.
- Developer: deploy and change apps (including Secrets), no RBAC or nodes. Can be limited to one namespace.
- Admin: full control of the whole cluster. Only for yourself.
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:
- Apps + volume data (Velero, recommended). A new cluster is created, then all app namespaces and their volume data (databases, uploads) are restored from the old cluster's Velero backup. Old backups are read through a second, read-only location so they are never overwritten; new backups go to the new cluster's own location. Old kubeconfigs stop working.
- Whole cluster state (etcd snapshot). Every resource comes back exactly, including the CA, so old kubeconfigs and kubectl access keep working and S3 backups continue in the same place. Old nodes are removed automatically. Local volume data on the old nodes is not included; pods using it wait until their volume is restored or recreated. This needs the source cluster token, which is backed up to S3 (encrypted) since 9 Oct 2026.
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.
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.