Database
Postgres for Feeblo. The DATABASE_URL shape, compose defaults, and the migration lifecycle from schema change to deployed schema.
Feeblo keeps all of its state in one PostgreSQL database. The compose stack runs
PostgreSQL 17 with the pgvector extension, and the server container applies migrations
itself on every start, so a production database mostly runs itself. The db: scripts
matter when you develop locally, manage the schema by hand, or recover a broken instance.
Connection string
The database URL follows the standard Postgres shape:
DATABASE_URL="postgres://feeblo:password@127.0.0.1:54323/feeblo"
| Part | Meaning |
|---|---|
feeblo |
user, POSTGRES_USER in compose |
password |
password, POSTGRES_PASSWORD in compose |
127.0.0.1 |
host the server can reach |
feeblo |
database name, POSTGRES_DB in compose |
Compose fills in sensible defaults for you:
- The
pgcontainer readsPOSTGRES_USER,POSTGRES_PASSWORD, andPOSTGRES_DBfrom.env, each defaulting tofeeblo,password,feeblo. - The server gets
DATABASE_URLdefaulting topostgres://feeblo:password@pg:5432/feeblo. The host ispgbecause that is the service name inside the compose network. - On the host, Postgres publishes to
${POSTGRES_PORT:-5432}:5432, so a local client connects to127.0.0.1:5432by default. The dev stack uses port54323instead.
Migration lifecycle
Schema lives in packages/db/src/schema/ (four files: auth.ts, feedback.ts,
integration.ts, media.ts). Drizzle generates versioned SQL migrations from them into
packages/db/src/migrations/.
Change the schema
Edit the Drizzle schema in packages/db/src/schema/. Add tables, columns, or indexes
the same way you would in any Drizzle project.
Generate the migration
pnpm db:generatedrizzle-kit generate diffs the schema against the last migration and writes a new
timestamped folder under packages/db/src/migrations/. Review the SQL it produced.
Commit the migration
Migrations are code. Commit the generated folder with the schema change so the next deploy carries it.
Apply it locally or deploy it
pnpm db:migratedrizzle-kit migrate applies every pending migration to the DATABASE_URL found in the
root .env file and records it in the __drizzle_migrations table.
Production never runs this command. The server image runs its own migration runner
(node ./migrate/index.js) before it starts listening, so a deploy applies pending
migrations automatically. See Deploy with Docker for the upgrade
flow.
The db: scripts
All scripts run from the repo root and load .env through dotenvx, so keep
DATABASE_URL in your root .env before running any of them.
| Script | What it does |
|---|---|
pnpm db:start |
Start the local database container (docker compose up -d) |
pnpm db:watch |
Same, with logs attached (docker compose up) |
pnpm db:generate |
Generate SQL migrations from schema changes |
pnpm db:migrate |
Apply pending migrations |
pnpm db:push |
Push the schema without a migration file (drizzle-kit push) |
pnpm db:seed |
Fill the database with sample data |
pnpm db:studio |
Open Drizzle Studio against the database |
pnpm db:nuke |
Erase every row in every table |
pnpm db:stop |
Stop the database container |
pnpm db:down |
Stop and remove the container (data volume survives) |
pnpm db:push is the fast path for throwaway development databases: it applies the
schema directly instead of going through migrations. On a shared or production database,
prefer generated migrations, because push leaves no migration record for other
environments to replay.
Local development stack
The dev compose file at docker/docker-compose.dev.yml defines Postgres, Redis,
MinIO, and Mailpit:
docker compose -f docker/docker-compose.dev.yml up -d
Postgres publishes on port 54323, which is exactly what the DATABASE_URL default in
.env.example points at (postgres://feeblo:password@127.0.0.1:54323/feeblo). A fresh
local checkout needs three commands:
pnpm db:migrate
pnpm db:seed
pnpm dev
pnpm db:seed creates an admin user and sample data so the dashboard has something to
show. Re-run db:migrate whenever you pull changes that contain new migrations.
If you change embedding dimensions, run pnpm db:configure-embeddings to resize the
vector column; the Docker page explains when that is needed.