Documentation
togo-framework/db

db

togo database stack — the togo-postgres image (ParadeDB: pg_search + pgvector + pg_analytics, plus pg_cron + pg_partman) and the batteries-included compose.

togo new --db togo-postgres provisions this stack. It is built on pgduckdb/pgduckdb:17 (PostgreSQL 17, Debian 12 bookworm) and ships:

Extension
Version
Notes
pg_duckdb1.1.1Real DuckDB embedded in Postgres — columnar analytics, Parquet/CSV/JSON reading
pg_search0.24.1ParadeDB BM25 full-text search
pgvector0.8.3Vector similarity search for RAG
pg_cron1.6.7Scheduled jobs
pg_partman5.4.3Partition management
Rows per page
1–5 of 5
Page 1 of 1

Published as a public image:

bash
ghcr.io/togo-framework/db:latest

Supabase compatibility

The image creates the Supabase service roles on first init (initdb/02-supabase-roles.sql): supabase_admin, authenticator, anon, authenticated, service_role, supabase_auth_admin, supabase_storage_admin — plus auth and storage schemas.

This means GoTrue, Supabase Storage, and Realtime can connect to this image without requiring supabase/postgres as the base.

Note: pgsodium, pg_graphql, and wrappers (Supabase-specific extensions) are not included because they are not available as standard packages for the pgduckdb/bookworm base. If you need those, use the supabase/postgres image instead and accept losing pg_duckdb.

Use it

bash
docker compose up -d
# DATABASE_URL=postgres://postgres:postgres@localhost:5432/togo

Or just the image (must set shared_preload_libraries):

bash
docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=postgres \
  ghcr.io/togo-framework/db:latest \
  postgres -c shared_preload_libraries='pg_cron,pg_duckdb,pg_search'

Required shared_preload_libraries

These extensions require preloading at server start:

code
pg_cron,pg_duckdb,pg_search

Set this in your docker compose command: or a custom postgresql.conf.

Smoke tests

sql
-- pg_duckdb: run a DuckDB analytics query
SELECT * FROM duckdb.query('SELECT 42 AS answer');

-- pg_search: BM25 full-text search
CREATE TABLE docs (id SERIAL PRIMARY KEY, body TEXT);
INSERT INTO docs (body) VALUES ('quick brown fox'), ('lazy dog');
CREATE INDEX docs_bm25 ON docs USING bm25(id, body) WITH (key_field='id', text_fields='{"body": {}}');
SELECT * FROM docs WHERE id @@@ paradedb.match(field => 'body', value => 'fox');

-- pgvector: nearest-neighbour search
CREATE TABLE vecs (id SERIAL, v vector(3));
INSERT INTO vecs (v) VALUES ('[1,2,3]'), ('[4,5,6]');
SELECT id, v <-> '[1,2,3]' AS dist FROM vecs ORDER BY dist LIMIT 1;