K3s with SQLite: Lightweight Control Plane, Zero High Availability
K3s defaults to a file-based SQLite store instead of etcd, cutting resource use on small hardware but creating a single point of failure. Learn how to verify the backend and when to plan for an external database.
08 May 2026, 02:24 UTC

Your edge node can run Kubernetes, but not two control planes
You install K3s on a Raspberry Pi or small VM because it promises a lightweight Kubernetes control plane. Everything works until you try to add a second server node for redundancy and find the cluster will not form a multi-master setup. The useful takeaway is simple: K3s uses SQLite by default, which keeps CPU and RAM low on constrained hardware, but SQLite is a single-node file store. It cannot serve as a shared database for multiple control-plane nodes, so high availability requires a different backend.
What SQLite replaces in K3s
Standard Kubernetes stores cluster state in etcd, a distributed key-value store. Etcd nodes must agree on changes through a quorum, which means managing certificates, network endpoints, and an odd number of members to avoid split-brain. That consensus machinery costs memory and CPU even when idle.
K3s swaps etcd for SQLite, a file-based database that lives on the local server node. There is no quorum to manage and no separate database process to run. This lowers the baseline resource footprint, which is why K3s is popular on edge devices and single-node homelabs.
Verify which storage backend is active
Run these checks on the K3s server node. You need root privileges, so use sudo or run as the root user.
- Check the version, because database defaults and flags changed across K3s releases:
# Run on the K3s server node
k3s --version
Permissions: Read access to the K3s binary; typically available to all users.
Expected check: Output shows the installed K3s version. Current releases default to SQLite unless an external datastore is specified at startup.
- Look for the SQLite database file after the server has started:
# Run on the K3s server node
sudo ls -l /var/lib/rancher/k3s/server/db/
Permissions: Root or sudo access to the /var/lib/rancher/k3s/ directory.
Expected check: A file such as k3s.db appears in the directory. If the directory is empty or missing, the server may not have initialized or may be using an external backend.
- Inspect the server logs for the backend announcement:
# Run on the K3s server node
sudo journalctl -u k3s | grep -i 'sqlite|etcd|datastore' | head -n 20
Permissions: Root or sudo access to systemd logs.
Expected check: Startup logs mention SQLite. If they mention an external endpoint or etcd, the node is not using the default local file store.
Risk: Do not delete or edit files under /var/lib/rancher/k3s/server/db/ while the server is running. Corrupting the SQLite file can make the cluster state unreadable.
Worked example: minimal install and a second-node test
This example confirms SQLite behavior on a fresh node. It does not test migration or high availability.
# Run on the intended server node
sudo k3s server --write-kubeconfig-mode 644
Where to run: The node that will host the single control plane.
Permissions: Root or sudo. The command starts a long-running daemon, so use a terminal multiplexer or systemd service in production.
Expected check: After startup, /var/lib/rancher/k3s/server/db/k3s.db exists and logs reference SQLite.
To confirm the HA limitation, try to start a second K3s server node pointing at the same cluster without an external database:
# Run on a second node; do not run on the first node
sudo k3s server --server https://FIRST_NODE_IP:6443 --token NODE_TOKEN
Placeholder: Replace FIRST_NODE_IP with the first node's address and NODE_TOKEN with the token from /var/lib/rancher/k3s/server/node-token on the first node.
Expected result: The second node may join as an agent, but it cannot become an additional server sharing the SQLite state. SQLite cannot replicate to another node, so there is no active-active control plane.
Limitation: single point of failure and write churn
Because the database is one file on one node, losing that node means losing access to cluster state until the node returns. If the disk is unrecoverable, you must rebuild the cluster and recreate workloads.
SQLite also handles concurrent writers less gracefully than etcd under heavy churn. Clusters creating and deleting many pods, secrets, or config maps per second may see increased latency on the local disk.
When you outgrow this model, K3s supports an external datastore such as PostgreSQL or MySQL, or a dedicated etcd cluster, at server startup. Moving from SQLite to an external backend is not an in-place conversion; you typically provision the new backend, restart the server with the datastore endpoint, and redeploy or rejoin workloads as needed. Check the K3s documentation for your version, because startup flags and supported databases have changed between releases.
Decision checklist
- Use default SQLite if you run a single node, a small edge gateway, or a test lab and can tolerate control-plane downtime.
- Plan an external database or etcd cluster if you need multiple server nodes, automatic failover, or production uptime.
- Verify the backend before adding nodes, not after discovering that a second server cannot share the local file.
Start with k3s --version and the directory check above. If the file exists and your architecture remains a single node, SQLite is likely sufficient. If you need redundancy, choose the external backend before you provision the second control-plane node.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.