Database · Postgres
Industric's only stateful dependency. What version, how the schema gets built and maintained, and the options for storing session recordings.
Durable control-plane state (users, tokens, audit events, deployments, runtime statuses) lives in PostgreSQL. Apply the backend schema with an explicit migration job before starting or upgrading the server. Normal startup loads the encryption keyring but performs no schema migrations.
Version & DSN
Use PostgreSQL 16. The connection string is supplied via EXO_DATABASE_DSN in either libpq keyword form or a URL:
host=db.example.com user=exo password=… dbname=exo port=5432 sslmode=requirepostgres://exo:…@db.example.com:5432/exo?sslmode=requireThe backend keeps a pool of up to 100 open / 10 idle connections.
Bundled vs managed
- Bundled (trials).
exo-installcan deploy an in-clusterpostgres:16-alpineStatefulSet and wire the DSN for you. The Docker Compose stack does the same locally. Fine for evaluation, not for production. - Managed (production). Point at RDS / Cloud SQL / a managed Postgres with
--postgres=false --db-dsn "…". You get backups, failover, and PITR from your provider.
Run migrations before serving
Run the target release's backend binary with the same database and encryption configuration as the serving process:
exo-server --migrations
The command applies pending schema work and exits. Wait for success before rolling out the server. In local source development, make db-migrate provides the migration entry point. The serving process does not silently repair an older schema; it can report query errors if this step was skipped.
In Kubernetes, keep application replicas at zero while a one-off Job runs the migration command from the target image with the application's Secret and ConfigMap. Wait for the database to be ready first. If migration fails, stop and inspect its logs; start application replicas only after success.
Partitioning & retention
Partition maintenance manages monthly children and retention for tables an operator has already partitioned. Migrations do not create partitions, and the maintenance loop skips non-partitioned tables. Review your database's actual layout before relying on partition-based retention.
Session-recording storage
By default, session recordings use local storage (EXO_CAST_STORE=local) under EXO_CAST_STORAGE_DIR (default /var/lib/exo/casts, up to 64 MiB per recording). In Kubernetes this path needs a writable volume. Mount a PersistentVolume if you want recordings to survive pod restarts.
1 volumeMounts:2 - name: casts3 mountPath: /var/lib/exo/casts4 volumes:5 - name: casts6 persistentVolumeClaim:7 claimName: exo-castsFor object storage, set EXO_CAST_STORE=s3 and EXO_CAST_S3_BUCKET, with credentials, region, and endpoint settings for your storage service. Back up and manage retention for your recording volume or bucket separately from Postgres and external log sinks.
Backups
Back up Postgres with your provider's tooling (or pg_dump for the bundled instance). Back up EXO_ENCRYPTION_KEY separately: a database backup is unreadable without it. See Config & secrets.