PostgreSQL Debezium Playground
Watch a three-member Patroni PostgreSQL 16 cluster fail over while Debezium keeps streaming outbox events to Kafka.

- eth0
- /
- docker
This playground requires a paid iximiuz Labs plan. It does not work on the free tier.
On the free tier, startup fails while building the Patroni image, because free-tier sessions cannot reach the PostgreSQL package repository (apt.postgresql.org). Free-tier VMs are also capped at 2 vCPUs and 4 GiB of RAM, half of what the stack needs.
What is running
One VM runs the full Compose stack from the project repository: three etcd voters, three Patroni/PostgreSQL 16 members, an HAProxy write endpoint, a KRaft Kafka broker, and a Debezium Connect worker streaming the app.outbox_events table to Kafka. The init tasks clone the repository into ~/postgres-debezium-playground, build the Patroni image, and run make up, so the stack is healthy when the terminal opens.
This is an educational single-host lab, not a production blueprint. Every role uses the password test.
Explore it
cd ~/postgres-debezium-playground
make status # etcd, Patroni roles, HAProxy, Kafka topics, connector, slot
make failover # stop the primary, watch a replica take over, rejoin the old one
make verify # full replication, CDC, promotion, rejoin, and persistence check
make logs # follow every service
Insert an outbox event through HAProxy, which always routes to the current primary:
./scripts/compose.sh exec -e PGPASSWORD=test postgres1 \
psql -h haproxy -U postgres -d playground -c "
INSERT INTO app.outbox_events (id, correlation_id, aggregate_type, aggregate_id, event_type, payload)
VALUES (gen_random_uuid(), gen_random_uuid(), 'orders', 'order-42', 'ORDER_CREATED', '{\"order_id\":\"order-42\"}');"
Read it back from the routed Kafka topic, keyed by aggregate ID, and press Ctrl+C to stop:
./scripts/compose.sh exec kafka /opt/kafka/bin/kafka-console-consumer.sh \
--bootstrap-server kafka:29092 --topic playground.events.orders \
--from-beginning --formatter-property print.key=true
Run make failover and insert again: the event still arrives, possibly with duplicates around the promotion, because delivery is at-least-once.
Connect from your own machine
The stack binds its ports to the VM's loopback interface only. Forward them over SSH with labctl, using the session ID from labctl playground list:
labctl port-forward <session-id> -L 5432:5432 -L 9092:9092 -L 8083:8083
Then use 127.0.0.1:5432 (database playground, user postgres, password test), Kafka at 127.0.0.1:9092, and the Connect REST API at http://127.0.0.1:8083. Patroni member APIs are on ports 8008 to 8010.
A real VM, not a container
Get root on a VM with its own kernel, so Docker, Kubernetes, and systemd just work.
Read the docs →
SSH from browser or CLI
Use the built-in web terminal, or connect with labctl ssh, plain ssh, scp, or rsync.
Read the docs →
Drive it with AI
Let Claude, Codex, or any MCP client start this playground and run commands in it.
Read the docs →
Expose HTTP(S) ports
Give any web app running inside the VM a public URL - for yourself or to share with others.
Read the docs →
Share terminals
Invite others into your terminal session, or open it yourself from another device.
Read the docs →
Customize with init scripts
Add shell scripts that run at boot to install packages, clone repos, or seed test data.
Read the docs →
Private networking
VMs reach the internet via NAT with no public IP; several VMs share bridge networks.
Read the docs →
Ephemeral or persistent
By default, VMs are destroyed when the session ends; enable persistence to keep the disks for next time.
Read the docs →
Built-in container registry
A private registry.iximiuz.com for every playground to share images across services and VMs.
Read the docs →