Stream database changes from PostgreSQL to Tinybird in real-time using Debezium Server. This demo shows how to capture inserts, updates, and deletes via Change Data Capture (CDC) and send them to Tinybird's Events API.
PostgreSQL (logical replication)
↓
Debezium Server (CDC capture & transform)
↓
HTTP POST to Tinybird Events API
↓
Tinybird Data Source (cdc_raw)Key Components:
- PostgreSQL 16: Source database with logical replication enabled
- Debezium Server 3.1: Captures changes and transforms them
- Tinybird: Real-time analytics platform (cloud for production and local for development)
Run the entire stack locally with Tinybird Local for development and testing.
- Docker (or OrbStack) and Docker Compose
- Tinybird CLI:
curl https://www.tinybird.co/download/cli | sh - Tinybird Local:
tb local startin your terminal after installing the Tinybird CLI
Step 1: Start infrastructure (Postgres + Tinybird)
docker-compose -f docker-compose-tblocal.yml --env-file .env.local up -dThis starts only PostgreSQL and Tinybird. Debezium stays stopped (it's in the cdc profile) because you need to first deploy the Tinybird project, get the append token, and pass it to the Debezium configuration.
Step2: Authenticate with Tinybird CLI
tb loginFollow the prompts to authenticate with Tinybird.
Step 3: Deploy the Tinybird project
tb deployThis creates the cdc_raw Data Source and generates a scoped debezium_write token with append-only permissions.
Step 5: Copy the token
tb token copy debezium_writeThis copies the token to your clipboard.
Step 6: Update .env.local with the token
Open .env.local and update the TINYBIRD_TOKEN value with the token from your clipboard:
TINYBIRD_TOKEN=<paste-the-token-here>
TINYBIRD_HOST=tinybird:7181Step 7: Start the CDC pipeline (Debezium)
docker-compose -f docker-compose-tblocal.yml --env-file .env.local --profile cdc up -dNow Debezium starts and begins capturing changes from PostgreSQL.
Step 8: Test the pipeline
# Connect to PostgreSQL
docker exec -it postgres-cdc-source psql -U postgres -d inventory-- Insert a new customer
INSERT INTO customers (first_name, last_name, email)
VALUES ('Test', 'User', 'test@example.com');
-- Update a customer
UPDATE customers SET first_name = 'Updated' WHERE email = 'test@example.com';
-- Delete a customer
DELETE FROM customers WHERE email = 'test@example.com';Step 9: Verify events in Tinybird
tb sql "SELECT fromUnixTimestamp64Milli(JSONExtractInt(payload,'__source_ts_ms')) __source_ts_ms, * FROM cdc_raw ORDER BY __source_ts_ms"Or type tb open and go to https://cloud.tinybird.co/local/7181/<your-workspace>/datasources/cdc_raw to see them inthe Tinybird Local UI.
Step 10: Monitor Debezium
docker-compose -f docker-compose-tblocal.yml --env-file .env.local logs -f debezium-server- Deploy the project to Tinybird Cloud with
tb --cloud deploy. - Get your host from
tb --output=json info | jq -r ".cloud.api"and copy the token fromtb --cloud token copy debezium_write. - Use these credentials to update your
.envfile for Debezium - Make the needed changes so that Debezium reads from your Postgres.
The cdc_raw Data Source receives events with this structure:
{
"id": 1,
"first_name": "John",
"last_name": "Doe",
"email": "john.doe@example.com",
"created_at": 1767959534139174,
"updated_at": 1767959534139174,
"__deleted": "false",
"__op": "c",
"__table": "customers",
"__source_ts_ms": 1767999538489
}Operation codes:
c= CREATE (insert)u= UPDATEd= DELETEr= READ (initial snapshot)
# Stop all services (local)
docker-compose -f docker-compose-tblocal.yml --env-file .env.local --profile cdc downCheck the logs:
docker-compose -f docker-compose-tblocal.yml --env-file .env.local logs debezium-serverCommon issues:
- "Datasource cdc_raw not found": The Data Source wasn't deployed. Run
tb deployagain. - "Invalid token": The token in
.env.localdoesn't match. Regenerate withtb token copy debezium_write. - Container exits immediately: Check if you started with
--profile cdcflag.
docker-compose -f docker-compose-tblocal.yml --env-file .env.local down
rm -rf debezium-data/offsets.dat
docker-compose -f docker-compose-tblocal.yml --env-file .env.local up -ddocker-compose -f docker-compose-tblocal.yml --env-file .env.local --profile cdc ps- PostgreSQL is configured with
wal_level=logicalto enable Change Data Capture - Debezium Server connects to PostgreSQL and monitors the Write-Ahead Log (WAL)
- Changes are captured and transformed using
ExtractNewRecordStateto flatten the event structure - Events are sent via HTTP POST to Tinybird's Events API endpoint
- Tinybird ingests the events into the
cdc_rawData Source in real-time
docker-compose-tblocal.yml- Local Tinybird setup with Docker Compose profilespg/init-db.sql- PostgreSQL initialization with sample tablestinybird/cdc_raw.datasource- Tinybird Data Source definition.env.local- Local environment variablesdebezium-data/- Offset tracking (auto-created)docker-compose.yml- Local Postgres and Debezium pointing to Tinybird Cloud
The local setup uses profiles for controlled startup:
- Default profile: Postgres + Tinybird (start first)
cdcprofile: Debezium (start after deploying Data Source)
This ensures the Data Source exists before Debezium tries to send events.
-
Improving the Tinybird project
-
Creating one Datasource per table with ReplacingMergeTree Engine.
-
Testing at a bigger scale