# Postgres and Supabase

Folio uses standard PostgreSQL, through the same `BoardStore` and source
interfaces as GitHub and SQLite. Supabase hosts Postgres; its Data API,
JavaScript SDK and Auth service are not required by this adapter.

The browser, remote CLI and MCP continue to use Folio's API. Database
credentials stay on the server. Login and source permissions are configured
separately from where cards are stored.

## Choose a database host before deploying

| Host | Advantage | Cost and limits |
| --- | --- | --- |
| **Supabase Free** | Managed Postgres and a shared connection pooler. | $0 on the Free plan; 500 MB database per project, 5 GB egress, up to 2 active free projects. May pause after a week of inactivity. Automatic backups are not included. |
| **Vanilla Postgres** | Use the same adapter on your own machine or another host. | PostgreSQL is free software. Hosting, uptime, TLS and backups depend on the machine or provider you choose. |

Supabase Free restricts excess usage rather than charging it. Confirm that the
organization is on Free before creating a project. Paid plans, custom domains,
dedicated poolers and the IPv4 add-on are separate choices; they are not needed
by this configuration. Sources: [pricing](https://supabase.com/pricing),
[cost controls](https://supabase.com/docs/guides/platform/cost-control).

## Configure a Postgres board

Set `FOLIO_BOARD_DATABASE_URL` securely in the server's environment. Its value
is a standard Postgres URL or libpq connection string. Keep passwords out of
`agents.yaml`, Git, commands, browser storage and logs.

```yaml
board:
  store:
    kind: postgres
    connection_env: FOLIO_BOARD_DATABASE_URL
    path: team-board
  server:
    bind: "127.0.0.1:4350"
    origins: ["http://localhost:4340"]
```

`path` is an opaque board id, not a filesystem path. It defaults to `database`
for `board.store`. The server creates the board with the standard columns on
its first use. An optional `board.source` imports a local seed once; later
changes to that folder do not update the database.

Start it with `folio-board-server api`. Local token authentication works
without GitHub. For a static client using that token, set `kanban.api.auth:
token` and provide its credential through the connection form. Hosted account
sign-in is an independent capability, described in [authentication](/docs/deployment/auth).

## Mix sources in a hosted account

```yaml
board:
  auth:
    provider: github
  sources:
    - id: team-board
      title: Team board
      store:
        kind: postgres
        connection_env: FOLIO_BOARD_DATABASE_URL
      access:
        readers: ["github:123456"]
        writers: ["github:234567"]
  server:
    bind: "0.0.0.0:10000"
    origins: ["https://example.github.io"]
    hosted:
      public_url: "https://board-api.example.com"
      github_client_id: "YOUR_GITHUB_APP_CLIENT_ID"
```

Use the user's stable numeric GitHub subject; permission defaults deny access.
Here the Postgres board id defaults to the source id, `team-board`. Add another
configured source for another board or database. GitHub repositories are
discovered independently. All authorized sources use the same canvas and
filtering, with per-source token permissions and revision checks.

## Connect with TLS

Remote connections require TLS with certificate and hostname validation.
The adapter does not fall back to plaintext. Local loopback or Unix sockets
can use `sslmode=disable` for a development database. Do not disable certificate
checks for a hosted database.

For Supabase, copy the **shared pooler, session mode** connection from Connect
in the dashboard when the backend needs IPv4. It supports persistent backends
and prepared statements on every plan; no IPv4 add-on is required.
See [connection methods](https://supabase.com/docs/guides/database/connecting-to-postgres).
Use the database or pooler credentials, not a Supabase publishable or service key.

The database role needs to initialize the private `folio_board` schema, or its
owner must initialize it first and grant that role the schema and table access.
Do not expose this schema to the Supabase browser Data API. Normal users reach
the board through Folio's authorized HTTP API.

## Persistence includes authorization

Postgres preserves cards across API restarts. A free API host's ephemeral
disk cannot preserve sessions, encrypted provider credentials or token
revocations: those need their own durable authorization store too. Do not
activate an account deployment with authorization data on an ephemeral disk.
Use [Postgres authorization storage](/docs/deployment/authorization) for an
ephemeral API host. Card and authorization adapters are chosen independently.
Changing storage does not provide Supabase Auth automatically.

For Render Free, the API sleeps after 15 idle minutes and may take about a
minute to wake. Local files are discarded. Its 750 free instance hours are
shared per workspace; transfer and build overages can be billed if a payment
method is present. Check billing before claiming a deployment costs $0.
See [Render's limits](https://render.com/docs/free).

## Backups and concurrency

Board writes run in a transaction and lock only that board's head. A stale
base revision or file hash fails without overwriting another writer. Identical
file content is stored once; revisions retain pointers to those blobs.
An uncertain write acknowledgement is never retried automatically.

Use `pg_dump` and `pg_restore` to preserve the private schema. With Supabase
Free, export data regularly and keep copies outside the project, as described
in [database backups](https://supabase.com/docs/guides/platform/backups).
Keep signing keys and encrypted authorization data backed up separately.

[Data storage](/docs/deployment/storage) compares all methods. [Remote CLI](/docs/deployment/tokens)
and [MCP](/docs/deployment/mcp) describe API clients; they do not edit SQL tables directly.
