Provision
When a project is created with data: { postgres: true }, MIOSA provisions a logical database in a managed Postgres cluster, generates credentials, and injects them as env vars.
const project = await miosa.projects.create({
name: "Smile Dental",
data: { postgres: true },
})
// Sandboxes and runtime instances under this project boot with:
// DATABASE_URL = postgresql://user:pass@host:port/proj_xxx You can also add Postgres to an existing project:
await miosa.projects.dataServices.create(projectId, {
type: "postgres",
size: "small", // small | medium | large
}) Connection pooling
MIOSA routes DATABASE_URL through PgBouncer in transaction-pooling mode by default. This means:
- Each runtime instance opens 5-10 connections to PgBouncer.
- PgBouncer multiplexes them over a much smaller pool of real Postgres connections.
- You can have 100s of runtime instances without exhausting Postgres’s
max_connections.
Transaction-pooling caveat: you can’t use session-level features like LISTEN/NOTIFY, prepared statements, or SET LOCAL. If you need those, request a session-pooler URL via DATABASE_URL_SESSION (coming soon).
Schema management is yours
MIOSA doesn’t run migrations for you. The recommended workflow:
- Use your framework’s migration tool (Prisma, Drizzle, Ecto, Alembic, etc.).
2. Run migrations as part of your build / startup, or as a separate one-off via:
await sandbox.exec({ cmd: "npx", args: ["prisma", "migrate", "deploy"] }) - Make migrations backward-compatible (expand → migrate → contract). This keeps rollback working — see Rollback.
Backups
MIOSA runs continuous WAL archiving plus daily base backups, both to durable replicated object storage. Retention defaults:
- Point-in-time recovery: 7 days
- Base backups: 30 days
Restore is a support request today; self-serve UI is coming soon.
Sandboxed development with the same DB
When you create a sandbox under a project that has Postgres, the sandbox also gets DATABASE_URL — pointing at the same managed database. Two options:
- Shared dev DB. Sandboxes and production both point at the production database. Fast iteration, risk of dev work affecting prod data.
- Per-environment DB. Production gets one DB, staging/dev get another. Configured via Environments.
Default is (2) once per-environment isolation lands. Until then, sandboxes share the production DB; use schema namespacing or be careful with destructive queries.
Connecting from a sandbox
await sandbox.exec({
cmd: "psql",
args: [process.env.DATABASE_URL, "-c", "SELECT 1"],
}) The sandbox’s DATABASE_URL is the same shape the runtime would see in production. Whatever code works in the sandbox works in the deployment.
Limits
| Tier | Storage | Connections (PgBouncer) | Memory |
|---|---|---|---|
| small | 1 GB | 100 | 512 MB |
| medium | 10 GB | 200 | 2 GB |
| large | 100 GB | 500 | 8 GB |
Storage scales automatically. Connection limits and memory are tunable per plan.
Costs
Postgres pricing is in two parts:
- Compute — per-hour while the DB is running (always-on by default for production; can be paused for sandboxed dev DBs).
- Storage — GB-month for data plus WAL.
See Usage & Billing. Per-database metering carries attribution, so platforms can charge back per end-customer.
Bring your own Postgres?
If you’d rather use an external Postgres, just set DATABASE_URL as an env var on the deployment and skip the managed offering:
await miosa.deployments.env.set(deploymentId, {
environment: "production",
vars: { DATABASE_URL: "postgresql://...your-external-db..." },
}) MIOSA’s runtime injects whatever you set. The managed Postgres is convenience; bringing your own is supported.
Python SDK
See also
- Overview — the broader data plane
- Environments — per-env credentials
- Rollback — why backward-compatible migrations matter