Skip to content

Storage

PlotPress keeps two kinds of state strictly separate.

Markdown, YAML, optional SQL fallback files. Lives in your repository, mounted into the container at /workspace. PlotPress treats this directory as read-only at runtime — the only way to change a dashboard is to change the files. This is the “config-as-code” surface.

Everything that has to outlive a single request:

TablePurpose
usersid, email, oidc subject, last seen
sessionssession token → user, expiry
email_otp_codesfor the built-in email OTP flow
audit_logone row per query execution
query_cacheoptional; keyed by (dashboard, view, params_hash, user_roles_hash)

A single SQLite file (/var/lib/plotpress/plotpress.db) holds all of it. WAL mode is on; back up with litestream or a periodic snapshot.

  • Zero ops. No separate process, no migration runner to schedule, no connection pool to tune.
  • Backup is a file copy. Litestream replicates to S3/R2 transparently.
  • Plenty fast. Audit and session writes stay well under SQLite’s single-writer ceiling for any team that fits in one Helm release.
  • Container-friendly. The default Helm chart mounts a single PVC; that’s the entire stateful surface.

Postgres is on the roadmap, not in v1. It becomes worth the operational cost when:

  • You need >1 replica of the backend.
  • Audit log retention grows past a few GB and you want partitioning / parallel queries.
  • You already run Postgres and want one fewer thing to back up.

The schema is intentionally portable. Migration will be a pgloader run plus a connection-string change.

  • Connection DSNs. Read from env at boot. Never written to SQLite.
  • Source data. Query results live in memory, optionally in query_cache with a short TTL, never in a long-lived table.
  • Workspace content. It’s already in git. Duplicating it would only invite drift.