# Render and Docker

One image runs the API and `/mcp`, and includes the `folio-board` remote CLI.
GitHub, SQLite and Postgres share the same source permissions and board
operations. Environment configuration names secrets; generated config does
not contain them.

## Choose persistence before deploying

| Deployment | Data | Cost and limits |
| --- | --- | --- |
| **Render Free + Supabase Free** | Postgres stores cards and authorization. Git remains authoritative for repository sources. | No paid disk. Supabase Free quotas and inactivity pauses apply; Render sleeps when idle. Check account billing and usage limits first. |
| **Docker + SQLite** | Named volume stores cards and authorization. | No hosting subscription when using a machine you already operate. You provide uptime, TLS and backups. |
| **Docker + vanilla Postgres** | Standard Postgres stores cards and authorization. | Same adapter as Supabase. Hosting and backups depend on your chosen machine or provider. |

## Render Free uses Postgres without a persistent disk

`render.yaml` selects **Free**, one Docker instance and `/healthz`. It registers
Postgres beside GitHub sources and uses the same database for authorization.
It creates no persistent Render disk or paid compute plan.

Set `FOLIO_BOARD_DATABASE_URL` securely in Render. For Supabase, use the shared
pooler in session mode; no paid IPv4 add-on is required. Confirm that the
Supabase organization is on Free before creating or selecting the project.
[Postgres](/docs/deployment/postgres) documents connections and quotas;
[authorization storage](/docs/deployment/authorization) documents sessions and tokens.

Keep the existing service URL, GitHub callback, App credentials and **current
`FOLIO_BOARD_SESSION_KEY`** when updating a service. Generate a key only for a
new installation. Select the reviewed branch before deploying; the Blueprint
normally follows `main` and auto-deploy waits for its checks.

The preset uses `FOLIO_BOARD_EPHEMERAL=true`. Startup refuses SQLite sources
and requires remote authorization storage in account mode. Local clones and
runtime configuration are disposable; cards and credentials stay in Postgres.

After deployment, the configured **Personal board** appears in the source
selector alongside authorized repositories. Set Postgres readers and writers
using stable numeric GitHub subjects. Access defaults deny other accounts;
**Settings → Sources** selects what the canvas loads.

Render Free sleeps after 15 idle minutes and may take about a minute to wake.
Its 750 free instance hours are shared per workspace. Transfer or build
usage can be billed when a payment method is present: verify billing before
claiming a deployment costs $0. Supabase Free can restrict excess usage and
pause after a week of inactivity. Sources: [Render Free](https://render.com/docs/free),
[Supabase Free](https://supabase.com/pricing), [Supabase costs](https://supabase.com/docs/guides/platform/cost-control).

## Docker Compose retains SQLite in a named volume

Copy `deploy/hosted/env.example` to `.env`, fill the App values and generate a
signing key once. Configure your public HTTPS API address and frontend origin.
Point an HTTPS reverse proxy at the container's loopback port.

```bash
cp deploy/hosted/env.example .env
openssl rand -hex 32
gh api user --jq .id
docker compose up --build -d
```

Put the generated key in `.env` as `FOLIO_BOARD_SESSION_KEY` and keep it across
replacements. Use the numeric id in `FOLIO_BOARD_SQLITE_WRITERS`, for example
`["github:123456"]`. Secrets, database files and private keys are excluded from
the image build context.

Compose persists `board-data` at `/home/board/data`. The entrypoint prepares
the directory, then runs the server and CLI as `board`. `docker compose down`
retains the volume; removing it removes its data.

To use Postgres instead, disable SQLite, enable Postgres and select its
connection variable for authorization as shown in the example's comments.
The connection can point to vanilla Postgres or Supabase. Remote connections
require verified TLS; loopback or Unix sockets can explicitly disable TLS for
local development.

| Location | Contents | Persistence |
| --- | --- | --- |
| `boards/<id>.sqlite3` and WAL files | SQLite cards and revisions | Named volume required |
| `.auth/security.sqlite3` and WAL files | SQLite authorization | Named volume required |
| Private Postgres `folio_board` and `folio_auth` schemas | Remote cards and authorization | Database host and backups |
| `clones/` and `project/agents.yaml` | Rebuildable clones and generated config | Disposable when using remote persistence |

## Runtime options keep providers separate

| Variable | Default | Meaning |
| --- | --- | --- |
| `FOLIO_BOARD_AUTH` | `github` | Web account login; `token` selects standalone bearer access |
| `FOLIO_BOARD_SQLITE` | `true` | Register the default SQLite board |
| `FOLIO_BOARD_POSTGRES` | `false` | Register the default Postgres board |
| `FOLIO_BOARD_POSTGRES_CONNECTION_ENV` | `FOLIO_BOARD_DATABASE_URL` | Name of the connection secret variable |
| `FOLIO_BOARD_POSTGRES_ID`, `FOLIO_BOARD_POSTGRES_TITLE` | `postgres`, `Postgres` | Stable source id and title |
| `FOLIO_BOARD_POSTGRES_BOARD` | Source id | Opaque database board id |
| `FOLIO_BOARD_SQLITE_ID`, `FOLIO_BOARD_SQLITE_TITLE` | `database`, `SQLite` | Stable source id and title |
| `FOLIO_BOARD_SQLITE_READERS/WRITERS`, `FOLIO_BOARD_POSTGRES_READERS/WRITERS` | `[]` | JSON arrays of stable subjects |
| `FOLIO_BOARD_AUTH_CONNECTION_ENV` | Absent | Select Postgres authorization; otherwise uses SQLite |
| `FOLIO_BOARD_AUTH_DB` | `<data>/.auth/security.sqlite3` | SQLite authorization file |
| `FOLIO_BOARD_EPHEMERAL` | `false` | Refuse SQLite and require remote account authorization |
| `FOLIO_BOARD_SOURCES` | `[]` | More sources through the common `board.sources` contract |
| `FOLIO_BOARD_DATA_DIR` | `/home/board/data` | Runtime directory; mount a volume when using SQLite |
| `FOLIO_BOARD_READ_ONLY` | `false` | Refuse card writes; databases must already exist |
| `FOLIO_BOARD_ORIGIN`, `FOLIO_BOARD_PUBLIC_URL` | Required | Frontend origin and public HTTPS API address |

GitHub account mode can enable both databases or only GitHub repositories.
Optional `FOLIO_BOARD_SQLITE_SEED` or `FOLIO_BOARD_POSTGRES_SEED` imports a
mounted board once. Additional sources use the same reader/writer rules.

Standalone `FOLIO_BOARD_AUTH=token` requires exactly one enabled database and
`FOLIO_BOARD_TOKEN` with at least 32 characters. It needs no GitHub credentials,
and provides no GitHub login or personal token issuance. The frontend selects
`kanban.api.auth: token` explicitly.

## CLI and MCP use the same endpoint

Issue a token in **Settings → API tokens**, then set `FOLIO_BOARD_API` and
`FOLIO_BOARD_TOKEN` in the tool environment. The token is never a command argument.

```bash
docker compose run --rm -e FOLIO_BOARD_API -e FOLIO_BOARD_TOKEN board folio-board board
```

A supplied command runs as `board` without requiring backend configuration.
For direct image use:

```bash
docker run --rm -e FOLIO_BOARD_API -e FOLIO_BOARD_TOKEN folio-board:local folio-board board
```

Readiness at `/healthz` is public; card and MCP requests require authorization.
[Remote CLI](/docs/deployment/tokens), [MCP](/docs/deployment/mcp),
[storage and backups](/docs/deployment/storage#back-up-the-authoritative-store).
