SQLite, Kubernetes & Litestream
I’ve been learning Kubernetes at home recently, and I’ve found a nice (but slightly dangerous!) pattern for hosting applications backed by SQLite in my homelab.
Table of Contents
Overview #
The pattern is relatively simple:
- No persistent volumes, just ephemeral ones
- No deployments, we use a statefulset instead
- No external database, we use litestream to backup/restore automagically
If this sounds familiar it’s because I’ve used this pattern for running SQLite-backed applications on fly.io before.
The obvious issue with this is that we risk losing any data written between Litestream replications if our cluster dies. Since I’m the only user and I have backups through litestream I’m fine with this risk level1.
Example manifest #
In this example we’ll deploy Kanboard, a basic Trello-like
application. I’m using ArgoCD & Kustomize to deploy this, but you can
just use kubectl apply -f example.yaml
as well. I will describe the
prerequisites after the yaml.
As we don’t use a persistent volume, any attachments used in Kanboard will disappear on restart, rescheduling of pod et c. Kanboard is not an ideal fit for this pattern! Linkding was much better :-)
The example goes the following:
- Create a namespace to run everything in
- Create a
ConfigMap
for litestream, which describes what database to backup, and where to backup/restore from - Create the
StatefulSet
, which includes an init container (which restores from backup if necessary) and Kanboard + Litestream in a pod. This also creates a ephemeral volume which the database is stored in. - Create a service for the STS
|
|
In order for this to work, you have to:
- Create a bucket to store the LiteStream backups in (you can use backblaze and their free tier for this)
- Create a secret to store this configuration in (we inject this secret into the LiteStream container in the manifest)
The secret needs to contain the following keys:
---
apiVersion: v1
kind: Secret
metadata:
name: litestream-s3
namespace: kanboard-sts
stringData:
# Bucket name
S3_BUCKET: ""
# Might look like: https://s3.us-west-000.backblazeb2.com
S3_ENDPOINT: ""
# Might look like this
S3_PATH: "kanboard_replica.sqlite3"
LITESTREAM_ACCESS_KEY_ID: ""
LITESTREAM_SECRET_ACCESS_KEY: ""
type: Opaque
Just apply the secret to your namespace.
Caveats / bootstrapping #
Update: Before I discovered -if-replica-exists
(available since
v0.5.0-beta1 from 2021!) for the restore
command, this section
contained a convoluted method to boostrap an application relying on
LiteStream. Now I just include said arguments to my restore command /
initContainer and call it a day.
Conclusion #
If you can live with the quirky bootstrapping process, it’s This
is a fairly simple deployment pattern for SQLite backed applications,
and I like to rely on it for my homelab stuff!
I’m also using PostgreSQL on my NAS for “serious” applications, as I’ve decided that having my Kubernetes cluster depend on it is fine! ↩︎