K3s Embedded SQLite Datastore: Single-Node Architecture Considerations
Architecture note on K3s embedded SQLite for single-node clusters: requirements, minimal design, trust boundaries, operational checks, failure modes, and migration triggers with qualified statements and uncertainty markers.
01 Jul 2025, 23:44 UTC

Requirements
A single-node K3s deployment using the embedded SQLite datastore runs on a Linux host with at least 512 MiB of RAM, a CPU capable of executing containerd, and network access to the Kubernetes API port (default 6443) for kubectl and external clients. The default installation uses systemd to manage the k3s service, but K3s can also operate under other init systems or inside containers; behavior in non‑systemd environments should be verified for the specific version in use.
Smallest Suitable Design
The minimal deployment is a single k3s server binary that bundles containerd, the Kubernetes control plane (API server, controller manager, scheduler), and an embedded SQLite database. All cluster state is written to /var/lib/rancher/k3s/server/db.sqlite. This removes the need to provision, secure, or back up an external etcd cluster for workloads that fit on one node.
Install with the default options (requires root or sudo):
curl -sfL https://get.k3s.io | sh -
The installer creates the k3s systemd service, places the kubeconfig at /etc/rancher/k3s/k3s.yaml, and starts the server immediately. In hardened environments where piping a remote script to sh is disallowed, download the script first, inspect it, then execute it.
Trust and Data Boundaries
The SQLite file at /var/lib/rancher/k3s/server/db.sqlite contains the entire cluster state: secrets, configmaps, resource versions, and runtime metadata. File permissions must restrict access to the k3s service user (typically root on default installs). Verify ownership:
ls -l /var/lib/rancher/k3s/server/db.sqlite
# Expected: -rw------- 1 root root ...
API server traffic is protected by mutual TLS between clients and the server. The server presents a certificate signed by the K3s CA; clients (including kubectl) present a client certificate also issued by that CA. The default kubeconfig at /etc/rancher/k3s/k3s.yaml embeds the CA cert, client cert, and key. Treat this file as a credential: do not copy it to untrusted hosts without rotating the client certificate and key.
Operational Checks
Service Health
systemctl status k3s
# Look for Active: active (running)
API Health
kubectl get nodes --kubeconfig /etc/rancher/k3s/k3s.yaml
# Should return the single node in Ready state
SQLite Size and I/O Latency
Monitor the database file size and disk I/O. The SQLite file size varies with the number and churn of cluster objects; a quiet single-node cluster may stay under 100 MiB, but active clusters can grow larger. Set alerts based on your own baseline:
- Disk usage on the partition holding
/var/lib/rancherexceeds 80 %. - Sustained I/O latency on the backing device (observable via
iostat -x 1) rises above a threshold you define for your storage; community observations often cite 200 ms as a starting point, but the exact value depends on hardware and workload.
Quick size check:
du -h /var/lib/rancher/k3s/server/db.sqlite
Backup
K3s does not automate SQLite snapshots. Implement a manual or scheduled backup. The safest method stops the service to guarantee a consistent file (downtime required):
systemctl stop k3s
cp /var/lib/rancher/k3s/server/db.sqlite /backup/db.sqlite.$(date +%F)
systemctl start k3s
For zero‑downtime backups, the sqlite3 CLI .backup command can be used while the server runs, but this requires the target filesystem to support atomic writes (not guaranteed on all filesystems, especially network mounts). Verify consistency after any restore.
Failure Modes and Design‑Change Triggers
Write Throughput Saturation
The embedded SQLite store is not intended for high‑throughput production workloads. Sustained heavy write load (for example, frequent secret updates, heavy CRD churn, or many pod status transitions) can saturate SQLite, causing increased API latency. The exact saturation point varies with storage hardware and workload pattern; community reports mention write rates on the order of 100 MiB/s as a rough indicator, but this is not a guaranteed K3s or SQLite boundary. If kubectl get events shows repeated Timeout or InternalError from the API server, investigate SQLite I/O among other possible causes. SQLITE_BUSY errors may appear in API server logs but are not guaranteed to be surfaced.
Node Count Scaling
SQLite is a single‑writer database. Adding worker nodes increases read traffic, but all writes still go through the single server. Beyond approximately 20 nodes the write path often becomes a bottleneck, though the actual limit depends on write intensity. A practical trigger is sustained API latency for write operations (create/update/patch) that exceeds a threshold you define (e.g., 500 ms) or leader‑election‑style failures appearing in the logs. These observations are not hard limits defined by K3s.
SQLite File Loss or Corruption
If db.sqlite is deleted, corrupted, or the underlying disk fails, the cluster state is lost. Recovery requires restoring from backup or rebuilding the cluster from manifests. There is no automatic replication. This is the primary reason to migrate to embedded etcd HA (multiple server nodes with Raft) or external etcd for any environment where data loss is unacceptable.
Migration Path
Moving from embedded SQLite to embedded etcd HA requires adding two more server nodes with the --cluster-init flag on the first and --server https://<first-server>:6443 on the others. K3s does not automatically migrate existing SQLite data to the new etcd cluster; you must back up the SQLite state (or reapply manifests) and restore it after the HA control plane is formed. For external etcd, pass --datastore-endpoint pointing to the etcd cluster at install time; existing SQLite data is not migrated automatically—reapply manifests.
Verification Checklist
- Confirm embedded DB is in use:
ps aux | grep 'k3s server'shows the server process (note: K3s also spawnscontainerdand other child processes) and/var/lib/rancher/k3s/server/db.sqliteexists owned by the k3s user. In non‑systemd environments, replace the systemd check with the appropriate process‑listing command. - Validate API accessibility and mutual TLS:
kubectl get nodes --kubeconfig /etc/rancher/k3s/k3s.yamlreturns success without certificate errors. - Test monitoring:
du -h /var/lib/rancher/k3s/server/db.sqlitereports size below your configured alert threshold. - Verify backup restore: on a test host, stop k3s, replace
db.sqlitewith a backup copy, start k3s, and confirmkubectl get all --all-namespacesmatches expected state.
Limitations
- No built‑in high availability; single point of failure at the database layer.
- No automated backup or snapshot mechanism.
- Write throughput limited by SQLite's single‑writer architecture.
- Not suitable for multi‑node control planes or clusters requiring zero‑downtime control‑plane maintenance.
Use this architecture for development, edge, IoT, or single‑application production workloads where operational simplicity outweighs availability and throughput constraints. Migrate to embedded etcd HA or external etcd when any trigger condition is met.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.