Skip to content
cloudhostDocs
Guide

Database

A serverless SQL database: it wakes on the first connection and sleeps when idle. Connect with any SQL client, branch from any moment of its history, and restore to any moment.

Create and connect#

cloudhost db create shop
# created database shop (branch main)
#   host      shop.database.trustpage.ir:5432
#   database  shop
#   user      app
#   password  …   (shown once)
#   url       postgres://app:…@shop.database.trustpage.ir:5432/shop?sslmode=require

Any SQL client or ORM works (psql, Prisma, Drizzle, pg, …). The user is always app, and the password is shown once. cloudhost db password shop makes a new one; the old one stops working within seconds.

Connections are encrypted (sslmode=require).

Each database takes a set number of connections at once (max_connections, 20 by default; 4 are kept for env.DB). A connection past that is not refused: it waits in line and goes in as soon as another one closes, and only gets 53300 too many clients if no slot frees up within 60 seconds. Keep your application's pool of idle connections small so it does not hold slots others need.

For many short connections (functions, serverless clients) use the pooled port 6432: the same connection string with :6432 instead of :5432 (pooled_connection_string in the create and new-password responses). There each transaction runs on one of the database's shared connections, so thousands of clients work over a few, and prepared statements work too. Session settings (SET outside a transaction), LISTEN and session advisory locks belong on 5432; a client that sends them on 6432 gets a connection of its own until it disconnects.

env.DB in functions#

type Env = {
  DB: {
    query(sql: string, params?: unknown[]): Promise<{ rows: Record<string, unknown>[]; rowCount: number }>;
    transaction(queries: { query: string; params?: unknown[] }[]): Promise<unknown[]>;
  };
};

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const name = new URL(request.url).searchParams.get("name");
    if (name) await env.DB.query("insert into guestbook (name) values ($1)", [name]);
    const { rows } = await env.DB.query("select name, at from guestbook order by id desc limit 5");
    return Response.json(rows);
  },
};

The function reads the database as env.DB: no password in the code and no connection to open or close. query(sql, params) runs one statement and transaction([...]) several together. Only your own team's databases can be bound.

Branches#

cloudhost db branch shop dev                                  # from main, now
cloudhost db branch shop yesterday --at 2026-11-03T09:30:00+03:30
cloudhost db branch shop fix --from dev
cloudhost db branches shop
cloudhost db branch delete shop dev

A branch is a copy-on-write copy of the database at a moment: made at once, taking no extra space until you write to it. Connect to it with the same host and password and the database name <branch>--<db>. Its starting point: the latest state (default), a time with a zone (--at) or a WAL position (--lsn).

  • Up to 50 branches and previews per database (backups apart).
  • Every app or function preview bound to the database has its own branch: preview-<branch>. Deleting the preview deletes that branch.
  • main, and a branch another branch starts from, cannot be deleted.

Restore to a point in time#

Restore
cloudhost db restore shop main --at 2026-11-04T09:30:00+03:30
# restored main to 0/1FA2728 (as of 2026-11-04T06:00:00.000Z)
# its state before the restore is kept as branch main-before-20261104t093512z

A restore moves a branch back to its state at a time (or WAL position). The branch keeps its name and address, and its state before the restore is kept as a backup branch <branch>-before-<time>; to undo, restore again from the backup or branch from it. Up to 20 backups per database.

How far back you can go depends on the plan; see pricing. Backups older than that are deleted, unless a branch starts from them.

Restore, deleting a branch, deleting a database and a new password are sensitive: from the dashboard they ask for a second factor. API keys do not need one.

Observability#

Compute, size, connections, cache hit ratio, transactions and the most expensive statements, in the dashboard, with the MCP tool get_resource_observability, or from the API. Nothing is recorded while the database sleeps.

API
curl -H "Authorization: Bearer $KEY" "https://api.ibel.ai/v1/db/projects/shop/observability?range=24h"

Deleting#

cloudhost db delete shop deletes the database with all its branches and history. It is refused while a live version (production or a preview) binds it.