Run Postgres, Valkey, and Qdrant in one Lovable app
Most Lovable apps start with one database and end up needing three. The transactional data lives in Postgres, the sessions and cache live in Redis or Valkey, the embeddings for AI features live in Qdrant. The default Lovable stack (Supabase only) handles one of those three. This post sets up all three in one shot, gives you one env block to paste, and gets you out of the multi-vendor signup pattern.
Contents
- Why three databases
- Provision the stack
- The env block
- Wire them into Lovable
- When you do not need three
- Local development with SpinDB
Why three databases
Each one solves a problem the others handle badly.
Postgres is the source of truth. User accounts, orders, content, anything you would write to a normal database. Use it for relational data, JSONB for occasional schema flex, and constraints for data integrity. Do not use it for hot session lookups or vector search.
Valkey (or Redis) is for things that need to be fast and can be lost. Sessions, rate limits, ephemeral caches, queues, ranked lists. Anything you can rebuild from Postgres if it disappears. The reason to keep this separate from Postgres is that session lookups happen on every request and beating them up against your transactional database hurts both sides.
Qdrant is for vector data, which is high-dimensional floating-point arrays with similarity search on top. Embeddings from OpenAI or local models, image features for visual search, anything where "find the most similar item" is the query. pgvector inside Postgres works at small scale; Qdrant scales further and handles filtered search better.
If your app does AI features (RAG, semantic search, recommendations) you almost certainly need all three eventually. Setting them up at the start is cheaper than retrofitting.
Provision the stack
Layerbase Cloud has a stack provisioning flow that creates multiple databases in parallel and hands you one env block when they are all ready. Visit layerbase.com/cloud/create/stack and pick the AI App Stack (Postgres + Valkey + Qdrant + optional auth) or build a custom one from the Web App Stack (Postgres + Valkey + Auth) plus a Qdrant.
The whole provisioning takes about 60 seconds because the databases come up in parallel, not in series.
If you would rather click them one at a time:
- Create a Postgres, name it
lovable-app-db. - Create a Valkey, name it
lovable-app-cache. - Create a Qdrant, name it
lovable-app-rag.
Either way works.
The env block
After the stack provisions, the dashboard gives you a .env block that looks like this:
DATABASE_URL=postgresql://layerbase:<password>@your-host.cloud.layerbase.dev:5432/app?sslmode=require
REDIS_URL=rediss://default:<password>@your-host.cloud.layerbase.dev:6379
QDRANT_URL=https://your-host.cloud.layerbase.dev
QDRANT_API_KEY=<long random string>Three env vars per database (Qdrant has two; the others have one). Copy the whole block.
Wire them into Lovable
In your Lovable project settings, paste each line into the env variable section. Then in your generated code, install the three clients:
pnpm add postgres ioredis @qdrant/js-client-restAnd create one shared module in lib/db.ts:
import postgres from 'postgres'
import Redis from 'ioredis'
import { QdrantClient } from '@qdrant/js-client-rest'
export const sql = postgres(process.env.DATABASE_URL!, { ssl: 'require' })
export const redis = new Redis(process.env.REDIS_URL!, {
maxRetriesPerRequest: 3,
})
export const qdrant = new QdrantClient({
url: process.env.QDRANT_URL!,
apiKey: process.env.QDRANT_API_KEY!,
})Now anywhere in the app you import { sql, redis, qdrant } from '@/lib/db' and use the right one for the job. That is the full integration.
A typical handler that uses all three:
import { sql, redis, qdrant } from '@/lib/db'
export async function searchAndRecord(userId: string, query: string) {
// Rate limit the search.
const count = await redis.incr(`search:${userId}`)
if (count === 1) await redis.expire(`search:${userId}`, 60)
if (count > 30) throw new Error('Rate limited')
// Embed and search.
const embedding = await embedQuery(query)
const results = await qdrant.search('documents', {
vector: embedding,
limit: 10,
with_payload: true,
})
// Record the search for analytics.
await sql`
insert into search_log (user_id, query, result_count, created_at)
values (${userId}, ${query}, ${results.length}, now())
`
return results
}Redis for the rate limit, Qdrant for the search, Postgres for the audit trail. Each one is the right tool, and the shared lib/db.ts keeps the imports clean.
When you do not need three
If your app does not have AI features, skip Qdrant. Two databases is plenty. If it is a tiny app and you do not have caching or sessions yet, one (Postgres) is plenty. The three-database setup is the destination, not the start.
The reason to provision all three at the start of a serious project is the friction cost of adding databases later. Once your app is on production traffic, swapping a session store from "Postgres rows" to "Redis keys" means a migration window. Doing it on day one means zero migration.
Local development with SpinDB
You can run all three locally with SpinDB:
npm i -g spindb
spindb create lovable-db --engine postgresql --start
spindb create lovable-cache --engine valkey --start
spindb create lovable-rag --engine qdrant --startThen point your .env.local at the local URLs:
DATABASE_URL=postgresql://localhost:5433/lovable_db
REDIS_URL=redis://localhost:6379
QDRANT_URL=http://localhost:6333
QDRANT_API_KEY=Three commands, three databases, no Docker. spindb stop <name> for each when you are done. What is SpinDB? has the rest.
Wrapping up
The short version:
- Provision the stack at layerbase.com/cloud/create/stack, or three databases one at a time.
- Paste the env block into Lovable.
- Install
postgres,ioredis, and@qdrant/js-client-rest. - Create one
lib/db.tsthat exports all three clients. - Mirror the setup locally with SpinDB.
The reason this works well for Lovable apps specifically is that the iteration loop is fast and you do not want to be three providers deep on day one. One signup, one dashboard, one bill for the database layer means more time on the app and less time on infrastructure plumbing.