Skip to main content
This walkthrough starts a self-contained CDC stack with pre-seeded PostgreSQL and Neo4j data. It bootstraps denormalized documents into OpenSearch, then shows inserts, updates, relationship changes, and deletes propagating from each source.
This guide covers the capture pipeline. For live events over WebSocket and GraphQL, use the real-time demo and Real-time subscriptions.
The demo runs the engines standalone. A running standalone process cannot be attached to VentStream Cloud in place. To use centralized configuration, visibility, and lifecycle operations, create a managed pipeline and deploy a new managed agent.
The sample data uses an e-commerce domain: Everything lives in demo/stack/: the docker-compose.yml, the seed data, and the two projection specs.

Prerequisites

  • Docker + Docker Compose v2 (docker compose version)
  • ~4 GB free RAM for the containers
  • Free ports: 5544 (pg), 7474/7687 (neo4j), 9200 (opensearch), 5601 (dashboards, optional)

Preflight: clean up and check port 9200

Start from a clean demo state so the seed counts and document IDs in this guide are deterministic:
This removes only the containers, networks, and volumes created by this Compose project. If you are intentionally resuming an earlier run, omit -v; the exact counts below may then differ if you changed or deleted sample data. The demo must have exclusive access to host port 9200. Check for another Docker container or local OpenSearch process before continuing:
Both checks should return no listener before you start the demo. On Docker Desktop, the demo container can sometimes appear to start even when a host process already owns localhost:9200. In that state, the verification curl commands may reach the wrong OpenSearch cluster. Stop the process shown by the checks above before continuing. For a Homebrew-managed OpenSearch, use brew services stop opensearch. For another Compose stack, run docker compose down from that stack’s directory. Do not kill an unrelated process without first identifying it.

1. Start the sources + target

--wait blocks until all three pass their healthchecks, so you don’t have to poll:
(Check status any time with docker compose ps.)
First boot pulls images (Postgres, Neo4j Enterprise, OpenSearch). Postgres seeds itself from seed/postgres.sql — 200 orders, 5 customers, line items, and the ventstream_shop publication.

2. Seed Neo4j + enable CDC

Postgres is already seeded. Neo4j needs two one-time commands — enable CDC, then load the catalog graph:

3. Start the engines

The engines build from source via cargo-chef. Allow 5–10 minutes for the first build, depending on CPU and network speed; later runs are cached and start in seconds.
Watch them bootstrap and switch to tailing. The demo enables debug logging so source progress, projection work, and sink batches are visible:
Each engine moves through its lifecycle:
1

STARTING

Process up, source connection opening.
2

BOOTSTRAPPING

Snapshot scan — existing rows/nodes stream into OpenSearch.
3

TAILING

Steady state — live CDC changes flow as they happen.

4. Verify the initial load

Inspect one denormalized order (Postgres source) — note the embedded customer and items:
Inspect one denormalized product (Neo4j source) — note the embedded category, supplierregion (2 hops), and tags:
The Postgres doc _id is the fully-qualified table name plus the PK as a JSON array: shop.orders:["ord-0001"] (URL-encoded above), so a GET …/_doc/<id> works directly. The Neo4j doc _id is products_denormalized:<elementId>, so search by a field (above) rather than guessing the elementId.

5. Watch changes propagate

Keep the engine logs visible while you run these. Each change should appear in the log within ~1s and update OpenSearch.
Update a row (1-hop)
Rename a customer (1:1 cascade) — every order for cust-002 recomposes:
Add a line item (1:many cascade)
Delete a primary row — items first (FK), then the order:

Stream it continuously (optional)

To see the pipeline move, fire updates fast and watch the engine react. We stream the statements into a single psql connection (a per-command docker exec would cap the rate at the container-exec overhead) and pace server-side with pg_sleep:
Or watch the document change in lockstep:
Ctrl-C to stop. Tune the pace with pg_sleep(0.1)0.02 for ~20 ms, or drop it entirely for max throughput. Each update recomputes only that one document; the dispatcher may combine nearby updates into one sink batch. Swap the WHERE to order_id='ord-'||lpad((1+floor(random()*200))::int::text,4,'0') to spread updates across all 200 orders.

6. Optional — inspection UIs

Open http://localhost:5601Dev Tools:
Always on at http://localhost:7474 (user neo4j, password ventstream). Explore the source graph:

7. Verify data flow

8. Teardown

-v drops the volumes (Postgres data, Neo4j store, engine state) and the next up re-seeds from scratch — omit it to keep your data. --remove-orphans clears leftover containers from old runs so a re-run doesn’t hit name/port clashes.

Next steps

Author a projection

The orders and products specs, field by field.

How fan-out works

Why renaming a category updates 500 docs without exploding.

Use VentStream Cloud

Enroll managed agents and administer pipelines from the dashboard or CLI.

Deploy to Kubernetes

Ship the whole stack with the bundled manifests.