Skip to content
All guides

servers

Postgres

On run(), if Postgres connects, Keel ensures the table:

CREATE TABLE IF NOT EXISTS items (
  id SERIAL PRIMARY KEY,
  title TEXT NOT NULL DEFAULT '',
  body TEXT NOT NULL DEFAULT '',
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
)

id and created_at are always added. You only name the text columns in api("items", "title, body").

Database URL

Order:

  1. store(url) if you called it
  2. BURAAQ_DATABASE_URL from the process, from ~/.buraaq/dock/env, or from an env file next to the binary
  3. DATABASE_URL
  4. Windows play fallback (localhost / buraaq_play)
  5. Linux localhost only if BURAAQ_PG_LOCAL=1: a shared VPS often already has someone else's Postgres on 5432
$env:BURAAQ_DATABASE_URL = "host=127.0.0.1 port=5432 dbname=app user=postgres"

Or store("...") in source before run(). Credentials belong on the host when you ship. Do not pack secrets into a .bur unless you intend that.

Managed Postgres works the same way. Neon and similar poolers need sslmode=require. Keel reconnects when the pooler drops an idle socket (/api/health is SELECT 1). Never commit the URL.

Linux hosts need libpq5. land.sh installs it. The runtime loads libpq.so.5 / libpq.dll at process start: it does not bake a password.

When to use std.db

connect, exec, quote, disconnect when the resource helper cannot express the query (joins, aggregates, admin scripts). For ordinary title/body lists, api() is the whole backend.

use std.db.{connect, exec, quote, disconnect}

fn main() {
    connect("host=127.0.0.1 port=5432 dbname=app user=postgres")
    exec("select 1")
    disconnect()
}

Quote user text with quote: never concatenate SQL.

Shipping

Postgres lives next to the dock, not inside the .bur. Dock copies ~/.buraaq/dock/env into the launched process. Same idea as not stuffing a database into a Docker image: the process is native; the data is the host's.