Quickstart
Get your first BunnyDB mirror running in under 5 minutes.
Prerequisites
Before you begin, ensure you have:
- Docker (version 20.10 or later)
- Docker Compose (version 2.0 or later)
- 4GB RAM minimum available for containers
- curl and jq for API testing (optional but recommended)
Quick Start
Clone the repository
# Clone the latest stable release
git clone --branch v1.0.0 --depth 1 https://github.com/Harshil-Jani/bunnyDB
cd bunnyDBStart all services
docker compose up -dThis command starts 8 containers:
catalog: Internal PostgreSQL database for BunnyDB statetemporal: Workflow orchestration enginetemporal-ui: Temporal dashboard for monitoring workflowsbunny-api: REST API serverbunny-worker: CDC worker processbunny-ui: Web dashboardsource-db: Test source databasedest-db: Test destination database
Wait for services to be healthy
docker compose psWait until all services show healthy or running status. This typically takes 30-60 seconds.
The catalog and test databases include health checks. Services depend on these being healthy before starting.
Access the UI
Open your browser to http://localhost:3000
Default credentials:
- Username:
admin - Password:
admin
Production Warning: Change BUNNY_JWT_SECRET and BUNNY_ADMIN_PASSWORD in your environment variables before deploying to production.
Create source peer (via API)
First, obtain an authentication token:
TOKEN=$(curl -s -X POST http://localhost:8112/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin"}' | jq -r .token)Then create the source peer pointing to the built-in test database:
curl -X POST http://localhost:8112/v1/peers \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "source",
"host": "source-db",
"port": 5432,
"user": "postgres",
"password": "sourcepass",
"database": "source_db"
}'Expected response:
{
"message": "Peer created successfully",
"peer": {
"name": "source",
"host": "source-db",
"port": 5432,
"database": "source_db"
}
}Create destination peer
curl -X POST http://localhost:8112/v1/peers \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "destination",
"host": "dest-db",
"port": 5432,
"user": "postgres",
"password": "destpass",
"database": "dest_db"
}'Prepare test data (optional)
Connect to the source database and create a sample table:
docker exec -it bunny-source-db psql -U postgres -d source_db -c "
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT NOW()
);
INSERT INTO users (email) VALUES
('alice@example.com'),
('bob@example.com'),
('charlie@example.com');
"Create a mirror
curl -X POST http://localhost:8112/v1/mirrors \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "my_first_mirror",
"source_peer": "source",
"destination_peer": "destination",
"table_mappings": [
{
"source_table": "public.users",
"destination_table": "public.users"
}
],
"do_initial_snapshot": true,
"max_batch_size": 1000,
"idle_timeout_seconds": 60,
"replicate_indexes": true
}'Key parameters:
do_initial_snapshot: Copy existing data before starting CDCmax_batch_size: Number of changes to batch togetheridle_timeout_seconds: Apply batch after this many seconds of inactivityreplicate_indexes: Replicate indexes from source to destination
Verify the mirror
Check mirror status:
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8112/v1/mirrors/my_first_mirror | jqYou should see output similar to:
{
"name": "my_first_mirror",
"source_peer": "source",
"destination_peer": "destination",
"status": "RUNNING",
"last_lsn": "0/123ABC",
"table_mappings": [
{
"source_table": "public.users",
"destination_table": "public.users"
}
],
"created_at": "2026-01-24T10:30:00Z"
}Mirror states:
CREATED: Mirror configuration createdSETTING_UP: Creating replication slot and publicationSNAPSHOT: Copying initial dataRUNNING: Actively replicating changes
Test live replication
Insert a new row in the source database:
docker exec -it bunny-source-db psql -U postgres -d source_db -c \
"INSERT INTO users (email) VALUES ('diana@example.com');"Verify it appears in the destination:
docker exec -it bunny-dest-db psql -U postgres -d dest_db -c \
"SELECT * FROM users WHERE email = 'diana@example.com';"The row should appear within seconds (depending on idle_timeout_seconds).
What’s Next?
Now that you have a working mirror, explore these topics:
- Architecture: Understand how BunnyDB works under the hood
- Concepts: Learn about peers, mirrors, signals, and LSNs
- API Reference: Explore all API endpoints for advanced operations
Monitoring
Temporal UI
Visit http://localhost:8085 to view Temporal workflows:
- See running
CDCFlowWorkflowinstances - Monitor workflow execution history
- Debug failed activities and retry attempts
Each mirror runs as a long-lived Temporal workflow that uses ContinueAsNew to avoid history limits.
BunnyDB Dashboard
The web UI at http://localhost:3000 provides:
- Real-time mirror status
- Recent sync statistics
- Error logs and alerts
- Quick actions (Pause, Resume, Resync)
Common Operations
Pause a mirror
curl -X POST http://localhost:8112/v1/mirrors/my_first_mirror/pause \
-H "Authorization: Bearer $TOKEN"Resume a mirror
curl -X POST http://localhost:8112/v1/mirrors/my_first_mirror/resume \
-H "Authorization: Bearer $TOKEN"Resync a specific table
curl -X POST http://localhost:8112/v1/mirrors/my_first_mirror/resync \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"table_name": "public.users",
"strategy": "swap"
}'The swap strategy ensures zero downtime during resync by using shadow tables.
Cleanup
To stop all services:
docker compose downTo remove all data volumes:
docker compose down -vTroubleshooting
Services won’t start
Check logs for specific services:
docker compose logs bunny-api
docker compose logs bunny-worker
docker compose logs catalogMirror stuck in SETTING_UP
Ensure the source database has logical replication enabled. The test databases include this by default, but external databases need:
-- In postgresql.conf
wal_level = logical
max_replication_slots = 10
max_wal_senders = 10Authentication errors
Verify your token is still valid. Tokens expire after 24 hours by default:
# Get a fresh token
TOKEN=$(curl -s -X POST http://localhost:8112/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin"}' | jq -r .token)Replication lag
Check Temporal UI for activity failures or retries. Common causes:
- Network latency between source and destination
- Destination database under heavy load
- Large transactions causing batch backlog
For production deployments, tune max_batch_size and idle_timeout_seconds based on your workload characteristics.