How this site works - SQLite + Cloudrun
- john
- March 20, 2025
This site is an experiment in running SQLite in production, with servers that auto-scale to zero.
The key point is to limit the site to a single instance, so any updates are going to one instance of the database.
Litestream is used to replicate the database to/from a storage bucket, providing backup, but also providing a mechanism to scale the app down to zero.
Benefits
- Cost: No need to have a database server running 24/7, so it should be possible to run the site within Google Cloud's free tier.
- Simplicity: The database is a plain file, so it's trivial to clone (e.g. to run local development or QA pull requests with a copy of the live database).
Limitations
- Litestream doesn't support multiple streams writing to one location, so we need to limit the site to a maximum of one instance.
- Because of is performance for high traffic (particularly a high number of concurrent writes) could be a problem.
- Note - for read-only access this can be mitigated by full-page caching (using Firebase hosting)
- Scale from zero needs to restore the database - which means cold boot is slow.
Performance / Timings
Currently, with a minimal setup (basic 1 CPU / 512MB) and no optimisation, and also with running manage.py migrate
at startup, the cold-boot time is quite slow, but after that performance is fine.
Example Cold boot timings
Cold boot, ~20 seconds
- 0.5 seconds for instance start
- 1-3 seconds
litestream restore
(restore database) - ~15 seconds
manage.py migrate
+ gunicorn start
Optimisation 1 - skip migrate based on build-time migrate --check
During build, run manage.py migrate --check
, and set an environment variable based on the result. We can then skip migrate at instance start if the environment variable is set.
Optimisation 2 - Cloud Run startup CPU boost
Cloud Run startup CPU boost adds extra CPUs during startup and for 10 seconds after instance start, but since this is extra CPUs, and it doesn't seem to increase the single CPU speed, it doesn't seem to help much.
Stack
- App: Puput , a Django/Wagtail-based blog app
- Database: SQLite, with Litestream replication to/from a Google Cloud Storage bucket
- Hosting:
- Docker on Google Cloud Run (auto-scaling limited to 1 instance)
- Firebase Hosting (to provide caching of pages and static files, domain mapping)
Inspiration & References
- https://minimalistdjango.com/databases/2023/06/18/sqlite
- https://tailscale.com/blog/database-for-2022/
- https://blog.expensify.com/2018/01/08/scaling-sqlite-to-4m-qps-on-a-single-server/
- https://simonwillison.net/2022/Oct/23/datasette-gunicorn/
- https://github.com/tomdyson/django-sqlite-load-tests
- https://alldjango.com/articles/definitive-guide-to-using-django-sqlite-in-production