Postgres

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: "Team Portal",
  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
})

Create it your way: API, CLI, or SDK

You don’t need an SDK. The same managed database can be created and used three ways. All of these hit the same API.

Raw API (curl):

# 1. Create a Postgres database
curl -X POST https://api.miosa.ai/api/v1/databases \
  -H "Authorization: Bearer $MIOSA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "my-db", "engine": "postgresql"}'
# → { "id": "db_...", "state": "provisioning", ... }   (reaches "running" in ~30-60s)

# 2. Get the connection string once it's running
curl https://api.miosa.ai/api/v1/databases/DB_ID/credentials \
  -H "Authorization: Bearer $MIOSA_API_KEY"
# → { "data": { "recommended_url": "postgresql://user:pass@host:port/miosa_db", ... } }

# 3. Connect with anything that speaks Postgres
psql "postgresql://user:pass@host:port/miosa_db"

Other engines: pass "engine": "mysql", "redis", or "qdrant".

CLI:

miosa api POST /databases -d '{"name": "my-db", "engine": "postgresql"}'  # create
miosa db list                     # see them + their state
miosa db connection <id>                    # open a shell to it
miosa db logs <id>                       # tail logs
miosa db backup <id>                     # snapshot
miosa api POST /databases/<id>/restore \
  -d '{"backup_id": "<backup-id>"}'          # restore

SDK (Python example below; TypeScript/Go/Elixir SDKs have the same methods).

Connection management

Use a bounded application connection pool and size it for the database plan. Treat the connection URL returned by the credentials endpoint as authoritative. Do not assume a separate session or transaction pooler unless the returned database configuration explicitly advertises one.

Where you can connect from

The connection URL points at a private MIOSA address. It is reachable from sandboxes, computers and deployments in your organization. It is not reachable from the public internet, so psql on your laptop cannot use it directly. To work with the database from your own machine, run psql inside a sandbox, or open a tunnel through one.

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 or startup, or as a separate one-off:
await sandbox.exec({ cmd: "npx", args: ["prisma", "migrate", "deploy"] })
  1. Make migrations backward-compatible (expand → migrate → contract). This keeps rollback working - see Rollback.

Extensions

These extensions can be used in a database: vector, pg_trgm, pgcrypto, uuid-ossp, citext, hstore, btree_gin, btree_gist and pg_stat_statements. Ask for the ones you want when you create the database:

miosa db create app-db --extensions vector,pg_trgm

Anything else (for example adminpack or file_fdw) is refused.

pg_stat_statements is on by default

Every new Postgres database has pg_stat_statements turned on, so the console’s top-queries view and your own SELECT ... FROM pg_stat_statements work from the first minute. It is added to the list you ask for, and pg_stat_statements.track is the Postgres default (top).

To create a database without it, send "pg_stat_statements": false when you create it:

miosa api POST /databases -d '{"name":"app-db","engine":"postgres","pg_stat_statements":false}'

Databases that already exist are not changed. To turn it on for one of them, enable the extension on that database; it takes effect the next time you restart it.

Backups

MIOSA supports scheduled backups and on-demand backups stored outside the database runtime. Retention depends on the database plan.

Create an on-demand backup:

miosa db backup <database-id> --json

Restore from a selected backup:

miosa api POST /databases/<database-id>/restore \
  -d '{"backup_id": "<backup-id>"}'

Restore replaces database contents. Review the target database and backup ID before confirming the operation.

Lifecycle and recovery

create creates provisioning; provisioning goes to running on ready; running goes to stopped on stop, and back to running on start.
running goes to restarting on restart or maintenance; restarting goes to running on verified.
restarting goes to error on recovery fails.
StateMeaning
provisioningMIOSA is creating storage, credentials, and the database runtime.
runningThe database accepts connections.
stoppedThe database was intentionally stopped. Persistent data remains attached.
restartingMIOSA is restoring the desired-running database after maintenance or a restart request.
errorRecovery did not complete and operator action is required.

During host maintenance, MIOSA preserves the database ID, credentials, persistent volume, bindings, and desired state. A database that was running is restarted and verified before its host returns to active service. An intentionally stopped database remains stopped.

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, connection, and memory limits depend on the database plan. Inspect the database response and metrics rather than assuming automatic storage growth.

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
  • MySQL: if you need MySQL compatibility instead
  • Environments: per-env credentials
  • Rollback: why backward-compatible migrations matter
Was this page helpful?