On this page

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:

  1. 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"] })
  1. 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:

  1. Shared dev DB. Sandboxes and production both point at the production database. Fast iteration, risk of dev work affecting prod data.
  2. 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

TierStorageConnections (PgBouncer)Memory
small1 GB100512 MB
medium10 GB2002 GB
large100 GB5008 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

Was this helpful?