PGlite (default for desktop)
PGlite is Postgres compiled to WebAssembly, running inside the orchestrator process. It writes to a directory on disk and serves SQL without a separate network hop. It is real Postgres, not a SQLite-style compromise, which means the schema, the queries, and the migrations are identical to the real-Postgres case. Data lives at:- A single user on a single machine
- Running a handful of companies
- Workloads below a few hundred concurrent tasks
- You have thousands of runs in the audit trail
- You run dozens of concurrent tasks
- You are generating large activity logs
Real Postgres
For real Postgres, point the orchestrator at a connection URL:DATABASE_URL and uses real Postgres
instead of PGlite. Everything else (schema, migrations, queries)
is identical.
Supported:
- Postgres 15 or 16 (we test against 16)
- Any standard Postgres distribution: the official Docker image, RDS, Cloud SQL, Neon, Supabase, Railway, Fly Postgres, whatever you prefer
- TLS via
PGSSLMODE=requireand friends
- Postgres 14 or older (missing features we rely on)
- Aurora Postgres compatibility mode (close but not exact, we have hit edge cases)
Schema
The schema is defined inpackages/db/src/schema/ using Drizzle.
There is one file per domain: companies.ts, agents.ts,
tasks.ts, runs.ts, leases.ts, costs.ts, memory.ts,
audit.ts, secrets.ts, integrations.ts.
Key tables you will care about:
companies— one row per companyteams— one row per team, foreign key to companiesagents— one row per agent, foreign key to teamstasks— the task queue, with state machine columnsruns— one row per run, foreign key to tasksleases— the active lease table; auto-expired by the lease reapercost_entries— every metered cost, for roll-upsaudit_events— the audit trail, append-onlysecrets— encrypted credential store
packages/db/ has an ER diagram if you prefer pictures.
Migrations
Migrations are Drizzle migrations. They live atpackages/db/drizzle/ and are numbered sequentially.
Run migrations manually:
Backups
PGlite
.tar.gz of the data directory plus the secrets
and the runs directory.
Real Postgres
Use whatever your Postgres provider gives you.pg_dump/pg_restore
for self-managed, snapshots for managed. The schema is standard
Postgres; there is nothing Company Agents-specific about how you
back it up.
We recommend:
- Hourly incremental backups (WAL archiving if you manage it yourself)
- Daily full dumps retained for 30 days
- Weekly full dumps retained for a year
- A restore drill at least quarterly (a backup you have never tested is not a backup)
Switching from PGlite to real Postgres
- Start your real Postgres (docker, managed, whatever)
- Stop the orchestrator
- Export from PGlite:
- Import into real Postgres:
- Set
DATABASE_URLand restart the orchestrator - Verify the dashboard loads and your companies are intact
- Delete the old
db/directory only after verifying the new install for at least a day
Switching from real Postgres to PGlite
Reverse of the above. The exporter supports both directions. This is rare (why would you?), but it is there for development and disaster recovery.Connection pool
The orchestrator uses a connection pool sized by the number of concurrent workers it expects to run. The default is fine for most deployments. If you run very high concurrency, tune:Next
- Secrets for how the encrypted secret store interacts with the database
- Storage for the other on-disk state (runs, workspaces, backups)
- Environment variables for all database-related env vars