Persistence
Configure database persistence—SQLite or PostgreSQL—paths, URLs, and migrations.
This page covers app.database: where the host stores application data (runs, users, credentials metadata, and related records).
It is not about file or blob storage (S3, uploads, attachments). Those are usually workflow or node concerns, not this config surface.
It also covers the storage backing for framework observability data such as traces, span artifacts, and metric points. Codemation stores those in the same database engine as the rest of the host state.
Codemation supports two database modes for consumer apps:
sqlitefor embedded local developmentpostgresqlfor shared environments and production
Local default: SQLite
SQLite is the shortest path to a working app on one machine.
Example:
app: {
database: {
kind: "sqlite",
sqliteFilePath: ".codemation/codemation.sqlite",
},
}This is a good default when:
- you are learning Codemation
- one developer is iterating locally
- you do not need multiple processes or shared access yet
sqliteFilePath is where the local database file lives on disk; it is still database persistence, not a general-purpose file store. Paths resolve relative to the consumer project root unless you pass an absolute path.
If you omit sqliteFilePath, Codemation falls back to .codemation/codemation.sqlite.
Shared environments: PostgreSQL
Switch to PostgreSQL when the app needs a shared database.
Example:
app: {
database: {
kind: "postgresql",
url: process.env.DATABASE_URL,
},
}The URL must be a postgresql:// or postgres:// connection string.
Typical environment setup:
DATABASE_URL=postgresql://user:password@127.0.0.1:5432/codemationAfter pointing the app at PostgreSQL, run migrations:
pnpm exec codemation db migrateA practical starter pattern
The generated templates switch between local SQLite and PostgreSQL based on REDIS_URL and DATABASE_URL:
const useRedisRuntime = Boolean(process.env.REDIS_URL);
const databaseUrl = process.env.DATABASE_URL?.trim();
if (useRedisRuntime && (!databaseUrl || databaseUrl.length === 0)) {
throw new Error(
"DATABASE_URL is required when REDIS_URL is set (BullMQ requires a shared PostgreSQL database).",
);
}
app: {
database: useRedisRuntime
? { kind: "postgresql", url: databaseUrl! }
: { kind: "sqlite", sqliteFilePath: ".codemation/codemation.sqlite" },
}That gives one simple progression:
- local SQLite while you are building
- PostgreSQL when the app becomes shared
- PostgreSQL plus Redis when you add queue-backed workers
Run retention vs telemetry retention
Run persistence and telemetry retention are related, but they are not the same policy.
You can configure the runtime so:
- debugger-friendly run state expires quickly
- telemetry spans and artifacts live longer for incident review
- aggregate metric points live longest for dashboards and usage trends
That separation works in both SQLite and PostgreSQL, so you can keep local development simple without designing yourself into a corner for production.
The host-level defaults come from environment variables:
CODEMATION_RUN_RETENTION_DEFAULT_SECONDSCODEMATION_BINARY_RETENTION_DEFAULT_SECONDSCODEMATION_TELEMETRY_SPAN_RETENTION_DEFAULT_SECONDSCODEMATION_TELEMETRY_ARTIFACT_RETENTION_DEFAULT_SECONDSCODEMATION_TELEMETRY_METRIC_RETENTION_DEFAULT_SECONDS
Workflows can still override the defaults with their own prune policy when a specific automation needs different retention windows.
Environment overrides
The host also supports environment-level overrides for database kind and paths:
DATABASE_URLfor PostgreSQL URLsCODEMATION_DATABASE_KINDto forcepostgresqlorsqliteCODEMATION_SQLITE_FILE_PATHto override the local SQLite database file path
These are useful when the same codebase needs different persistence in local, staging, and production environments.
BullMQ compatibility rule
If you move to queue-backed scheduling, do not keep SQLite.
BullMQ requires:
- Redis for queues
- PostgreSQL for shared persistence
That means this combination is valid:
REDIS_URL=redis://127.0.0.1:6379
DATABASE_URL=postgresql://user:password@127.0.0.1:5432/codemationThis combination is not:
- Redis queues with local SQLite only
app.database vs runtime.database
Most consumer apps should configure persistence through app.database.
Use runtime.database only when you need the lower-level runtime shape directly. The consumer-facing app form is the recommended path for templates and normal apps.