Docs › Database
Database
Garden CMS stores pages, themes, collections, content blocks and settings in a relational database. DATABASE_URL chooses which:
DATABASE_URL |
Database |
|---|---|
(unset) or sqlite:///data/garden.db |
SQLite file, relative to the app |
sqlite:////var/lib/garden/garden.db |
SQLite file, absolute path |
postgresql://user:pass@host:5432/garden |
PostgreSQL |
Which one?
SQLite is a single file: nothing to install, trivial to back up, ideal for local work and small single-server sites.
PostgreSQL is the choice for managed or serverless hosting (Neon, Supabase, RDS…), several app instances, or STATELESS=true deployments. The app keeps a small connection pool tuned for serverless Postgres, whose compute suspends when idle.
Migrations
The schema is created and upgraded by migrations — the same ones for both databases:
uv run piccolo migrations forwards db
The container image runs this automatically on start. Migrations are idempotent: running them again does nothing.
Export and import
Your content can be written to a directory of plain files — and read back:
uv run python -m features.portable export content/
uv run python -m features.portable import content/ # dry run
uv run python -m features.portable import content/ --apply
An export contains every theme (template.html, style.css, head.html), page (.html body plus .json settings), collection and item, content block, setting and markdown document. Records refer to each other by slug, never by database id, and JSON is sorted — so an export is stable and reads well in git diff.
Uses:
- Version your site in git — export, commit, review changes like code
- Move between databases — export from SQLite, import into Postgres (or back)
- Promote content — import a directory prepared elsewhere into production
Imports create and update; they never delete. Without --apply you only see the plan:
create theme mycelium-term
update page home
create markdown docs/database.md
S3 credentials (s3_access_key_id, s3_secret_access_key) are left out of exports; pass --include-secrets if you really want them. Uploaded media files themselves stay in storage — the export lists their metadata.