Drasi is an open-source platform for change-driven systems: applications that must notice a specific change in data and react to it quickly. Instead of polling a database on a timer, or building a stream-processing job, you write a continuous query, a declarative graph query that Drasi keeps running forever. Drasi watches the change feeds of your sources, keeps the query’s result set current as data changes, and tells a reaction exactly what was added to, updated in or removed from that result. The reaction can notify a dashboard, publish to a message bus, call a webhook, run a stored procedure, or feed an AI agent. Microsoft’s Azure Incubations team created Drasi, released it as open source in October 2024 under the Apache 2.0 licence, and it was accepted into the CNCF Sandbox on January 21, 2025. This brief explains Drasi from the problem it solves to installing, using, securing and operating it, and states plainly which sources and reactions exist and which do not, so one read is enough to decide whether it belongs in your platform.
CNCF daily · Sprint 4, post 5 of 15 · Streaming & Messaging
At a glance
| Category | App Definition and Development › Streaming & Messaging |
| CNCF status | Sandbox. Accepted January 21, 2025 |
| Maker | Microsoft Azure Incubations, with community contributors |
| Written in | Rust for the core engine, server and control plane; C# for much of the platform’s reaction tooling |
| License | Apache 2.0 |
| Query languages | openCypher (a subset, the original language) and GQL, the ISO graph query standard published in April 2024 |
| Ways to run it | Drasi for Kubernetes (the platform, installed with the drasi CLI), Drasi Server (a standalone process with a REST API) and drasi-lib (an embeddable library with Rust, Node.js, Python and .NET bindings) |
| Sources documented | PostgreSQL, MySQL, SQL Server, Azure Cosmos DB (Gremlin API), Kubernetes, plus Azure Event Hubs and Microsoft Dataverse in the launch material |
| Reactions documented | Debug, Result, SignalR, Azure Event Grid, Azure Storage Queue, Stored Procedure, Dapr pub/sub, Dapr state store sync, and an MCP reaction |
| Dependencies on Kubernetes | Dapr, Redis, MongoDB and an OpenTelemetry collector, installed by drasi init |
| Extensibility | Custom sources and reactions through SDKs |
The problem Drasi solves
Many systems need to answer “tell me when X becomes true”: a freezer’s temperature has stayed above a limit for fifteen minutes, an order is ready and a driver is free, a deployment contains a container image with a known vulnerability. The usual answers all have a cost:
- Polling runs a query every few seconds. It is simple, but it wastes load, adds latency equal to the interval, and cannot tell you what changed, only what the answer is now.
- Triggers and
LISTEN/NOTIFYfire on every row change in one database, and you still have to write the logic that decides whether the change matters, across tables, and across databases. - Stream processing with a framework works, but you operate a cluster, write code, and move data into a stream first.
- Change data capture into a broker moves the raw changes. It does not decide which of them matter.
Drasi’s idea is to describe the condition as a query and let the platform do the rest. The query result is a live view. When a change makes a new row appear in that view, or alters or removes one, Drasi emits just that difference. The data stays where it is: Drasi tracks the change logs and feeds of the sources and does not copy the data into a lake.
Key concepts
- Source. A connection to a system that produces changes, such as a PostgreSQL database read through logical replication. A source also supports a bootstrap, a read of the current data so a new query starts from correct state.
- Continuous query. A query that Drasi evaluates incrementally for as long as it exists, over a virtual property graph built from one or more sources. Rows become nodes, and relationships link them, either real foreign keys or synthetic relationships you declare across sources.
- Result set and result change. The query’s current output, and the stream of added, updated (with before and after values) and deleted results as it evolves. Drasi calls a change to the result set a “diff” in its engine.
- Reaction. A component that receives result changes from one or more queries and acts. Reactions are the integration point with the outside world.
- Query container. The runtime that hosts continuous queries on Kubernetes. A default container is created at install time; you can add more.
- Middleware. Per-source transformations, such as mapping an incoming event type to a graph label, applied before a query sees the data.
- Drasi functions. Extensions to Cypher for temporal reasoning:
drasi.trueFor,drasi.trueLater,drasi.previousValue,drasi.previousDistinctValue,drasi.changeDateTime,drasi.linearGradient,drasi.getVersionByTimestampanddrasi.getVersionsByTimeRange. - Resources. On Kubernetes you declare Sources, ContinuousQueries and Reactions as YAML documents with
apiVersion: v1, applied withdrasi apply, not withkubectl.
Architecture
Drasi on Kubernetes is a control plane plus three kinds of workloads: sources, query containers and reactions. Blue is a database or the CLI, purple a Drasi component, grey the control plane, green state and messaging, and orange a consumer. Open the zoomable diagram, or download it as SVG or PNG.
| Component | What it does |
|---|---|
| Drasi CLI | Installs Drasi (drasi init), applies and deletes resources, waits for readiness and lists what is running |
| Management API and resource provider | Accept resource definitions and have the Kubernetes provider create or update the matching workloads |
| Source (reactivator, proxy, change router) | A reactivator reads the external change feed and converts it to a standard change event; a proxy serves the bootstrap read; the change router publishes events |
| Query container | A query host evaluates continuous queries; a view service stores each query’s current result and serves it |
| Reaction | A workload per reaction that subscribes to queries and acts on every result change |
| Dapr, Redis, MongoDB, OpenTelemetry | Dapr pub/sub over Redis carries events, MongoDB holds state, and the collector gathers telemetry |
The source model is borrowed from change data capture. Its change events look like Debezium’s, and the PostgreSQL source reads the write-ahead log through logical replication. Because Drasi never copies the whole dataset, a query that joins two databases works by keeping just the elements it needs in its own working state.
From a row update to a notified consumer
- A row changes. An application updates a row in a monitored table.
- The source decodes it. The source reads the change from the write-ahead log and turns it into a change event for the graph node that the row represents.
- The query reads its state. The continuous query receives the event and looks up the elements it holds for the affected node.
- The query stores the new result. It re-evaluates only what the change can affect and stores the new result.
- Only the difference is sent. If the change added, updated or removed a result row, the query emits that result change to its reactions. A change that does not affect the query result produces nothing.
- The reaction acts. The reaction notifies the consumer, for example by publishing a message or updating a dashboard.
Recap: you declare the condition once; Drasi emits only transitions into, within and out of it.
Sources, reactions and what is not supported
Verify against the current documentation before you design around any of this, because the catalogue grows. As of the documentation and announcements I checked:
Sources for Drasi for Kubernetes: PostgreSQL, MySQL, SQL Server, Azure Cosmos DB through its Gremlin API, and Kubernetes resources. Azure Event Hubs and Microsoft Dataverse were named in the launch announcement. The Drasi Server and drasi-lib family has its own plugin set, published to the ghcr.io/drasi-project registry, including PostgreSQL, HTTP, gRPC and mock sources, and it installs plugins by reference.
Reactions for Drasi for Kubernetes: Debug (a web view of results for development), Result (a REST API to read current results), SignalR, Azure Event Grid, Azure Storage Queue, Stored Procedure, a Dapr pub/sub reaction, a Dapr state-store sync reaction, and an MCP reaction. Drasi Server lists HTTP (and an adaptive HTTP with retries), server-sent events, gRPC (and adaptive gRPC), a log reaction and a platform reaction that writes CloudEvents to Redis Streams.
What is not there. I did not find first-party sources for MQTT brokers, Kafka topics or Google Cloud Pub/Sub, and no first-party reaction for Google Cloud services. Nothing prevents using them indirectly: the Dapr pub/sub reaction takes a component name, so any pub/sub component Dapr supports can in principle receive the output, and you can write a custom source or reaction with the SDKs (Drasi ships a .NET and a Python reaction SDK and a source SDK). Treat that as engineering work, not a configuration option.
Query language limits. Drasi supports a subset of openCypher chosen for incremental evaluation: MATCH, WHERE, RETURN and WITH, the aggregates count, sum, avg, min and max, coalesce, relationship patterns and the Drasi functions above. Clauses such as ORDER BY and LIMIT, and DISTINCT, are not part of the supported subset in the references I checked, and collect has been an open item, so sort and limit in the consumer. Check the Cypher reference page for your release.
Install and first run
Drasi for Kubernetes
Prerequisites: a Kubernetes cluster (the project was tested from v1.22 upward, and local options such as kind, k3d and Docker Desktop work), kubectl pointed at it, and Helm v3 for the Dapr installation.
- Download the
drasiCLI for your platform from the project’s GitHub releases and put it on your path (on Windows rename the filedrasi.exe). - Point the CLI at the cluster and install:
drasi env kube # use the current kubectl context
drasi init # installs Drasi into the drasi-system namespace
drasi init creates the namespace, installs the Dapr runtime if it is missing, deploys Redis, MongoDB and an OpenTelemetry collector, installs the control plane, creates a default query container and applies the default source and reaction providers. Useful options:
| Option | Meaning |
|---|---|
--namespace, -n |
Install into a namespace other than drasi-system |
--version |
Image tag to install (default is the latest) |
--registry |
Pull Drasi images from your own registry, for air-gapped or controlled environments |
--local |
Use images already on the node, for local development |
--manifest <dir> |
Write the Kubernetes and Drasi manifests to a directory instead of applying them, so you can review and apply them through your own pipeline |
--dapr-runtime-version, --dapr-sidecar-version, --dapr-registry |
Pin or mirror the Dapr components |
- Check the pods in
drasi-system: expectdrasi-mongo-0,drasi-redis-0, anotel-collector,drasi-api,drasi-resource-providerand adrasi-querypod.
kubectl get pods -n drasi-system
To view results with the Debug reaction from outside the cluster, install an ingress with drasi ingress init (add --local-cluster for kind, and see the ingress reference for k3d), or port-forward the reaction’s service.
Drasi Server and drasi-lib
If you do not want Kubernetes, Drasi Server is a single process configured with one YAML file and managed through a REST API with Swagger documentation. You declare sources, queries and reactions in the file; changes made through the API are written back to the file when it is writable. The server has no built-in API authentication, and its documentation recommends putting a reverse proxy in front of it. drasi-lib goes further and embeds the engine in your own process, in Rust, Node.js, Python or .NET, with no separate infrastructure. Both are newer than the Kubernetes platform and their configuration schema has changed between releases, so use the documentation for the version you install.
A worked example
This example uses a neutral scenario: devices report a temperature into a PostgreSQL table, and we want to know when a device has been too hot for five minutes.
Prepare PostgreSQL
The PostgreSQL source reads logical replication, so the database needs wal_level = logical and enough replication slots and WAL senders, and the user Drasi connects with needs replication permission and SELECT on the tables:
CREATE TABLE device (
id text PRIMARY KEY,
site text NOT NULL,
temp double precision NOT NULL,
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE ROLE drasi_user LOGIN PASSWORD 'change-me' REPLICATION;
GRANT SELECT ON device TO drasi_user;
Keep the password in a Kubernetes Secret in the Drasi namespace:
kubectl create secret generic pg-creds -n drasi-system --from-literal=password='change-me'
Declare the source
apiVersion: v1
kind: Source
name: plant-db
spec:
kind: PostgreSQL
properties:
host: postgres.default.svc.cluster.local
port: 5432
user: drasi_user
password:
kind: Secret
name: pg-creds
key: password
database: appdb
ssl: true
tables:
- public.device
The tables list names each monitored table; rows of public.device appear in queries as nodes labelled device. Apply it and wait for it to become ready:
drasi apply -f source.yaml
drasi wait -f source.yaml -t 120
drasi list source
Declare the continuous query
apiVersion: v1
kind: ContinuousQuery
name: devices-too-hot
spec:
mode: query
sources:
subscriptions:
- id: plant-db
query: >
MATCH (d:device)
WHERE drasi.trueFor(d.temp > 80.0, duration({ minutes: 5 }))
RETURN d.id AS id, d.site AS site, d.temp AS temp
drasi.trueFor makes a device appear in the result only after the condition has held for five minutes without a change that made it false, which is the part that polling cannot do cleanly. A device that cools down is deleted from the result, and the reaction learns about that too. If you prefer GQL, add queryLanguage: GQL and write the query in that language; Cypher is the default.
Declare a reaction and watch it
apiVersion: v1
kind: Reaction
name: debug
spec:
kind: Debug
queries:
devices-too-hot:
drasi apply -f query.yaml -f reaction.yaml
drasi wait -f query.yaml -t 120
drasi list query
Open the Debug reaction’s address and update a row, for example UPDATE device SET temp = 90 WHERE id = 'device-001';. After five minutes the device appears in the view, and if its temperature drops it leaves. The Debug reaction is for development; it is not a production interface.
Join across sources
A query can span several sources by declaring a synthetic relationship, which gives you a join without a physical foreign key:
apiVersion: v1
kind: ContinuousQuery
name: orders-ready-with-driver
spec:
mode: query
sources:
subscriptions:
- id: orders-db
- id: drivers-db
joins:
- id: ASSIGNED_TO
keys:
- label: Order
property: driver_id
- label: Driver
property: id
query: >
MATCH (o:Order)-[:ASSIGNED_TO]->(dr:Driver)
WHERE o.status = 'ready' AND dr.status = 'available'
RETURN o.id AS order_id, dr.id AS driver_id
The relationship ASSIGNED_TO exists wherever Order.driver_id equals Driver.id, even though the two labels come from different databases. Avoid joins that match everything with everything, because the query keeps the working state it needs in memory.
A production reaction
For a real consumer, use a reaction that delivers somewhere durable. The SignalR reaction takes its connection string from a Secret in the same way as the source password:
apiVersion: v1
kind: Reaction
name: dashboard
spec:
kind: SignalR
queries:
devices-too-hot:
properties:
connectionString:
kind: Secret
name: signalr-creds
key: connectionString
For a message bus, use the Dapr pub/sub reaction with a Dapr component of your choice, or the Drasi Server HTTP, gRPC or platform reactions, or write a reaction with the SDK that delivers to your system.
Production reference design on Kubernetes
- Isolate Drasi. Install into its own namespace (
drasi init -n drasi-systemis the default), pin the version with--version, and mirror the images to your registry with--registryif the cluster cannot reach the public one. Generate manifests with--manifestwhen your change process requires review before applying. - Give Drasi a dedicated database role. Replication permission and
SELECTon exactly the tables in the source, nothing else. Usessl: trueand a verified certificate path where the database supports it, and a network policy that allows only the source pod to reach the database. - Choose source tables deliberately. Monitor ordinary tables that hold the state you reason about, not a firehose of raw events. A very high-volume append-only table is better served by a stream processor. If your database is a PostgreSQL extension that stores rows in internal child tables (a time-series hypertable, for example), check logical replication behaviour for it before you rely on it as a source.
- Plan for the replication slot. A logical replication slot retains write-ahead log until the consumer confirms it. A source that is stopped or broken makes the slot hold WAL and can fill the primary’s disk, so alert on slot lag and on retained WAL, and delete the slot when you retire a source. Logical slots live on the primary, so understand what your database operator does to them during failover.
- Keep resources in Git. Source, ContinuousQuery and Reaction files are the configuration. Apply them with
drasi applyfrom your pipeline, in the order source, query, reaction, and rundrasi waitbetween steps. Do not usekubectlto manage them. - Make consumers idempotent. Treat reactions as delivering at least once. Design the consumer so that a repeated added, updated or deleted result is harmless, and decide how to replay after an outage, since a query’s current result is available from the Result reaction.
- Back the infrastructure. The default install runs one Redis and one MongoDB. Give them persistent volumes, back them up as you would any state, and decide whether the single-instance default meets your availability needs.
- Test with the Debug reaction, ship with a durable one. Remove the Debug reaction and its ingress before production, because it exposes query results.
Security
- Secrets. Reference credentials as Kubernetes Secrets in the Drasi namespace, as in the examples; do not put passwords in YAML in Git.
- Least privilege at the source. A dedicated replication user with
SELECTon the needed tables. Remember that Drasi will see every column of the tables it reads, so expose only tables, and where possible views or narrower tables, that hold what the queries need. - Network. Use network policies around the
drasi-systemnamespace; Dapr provides mutual TLS between sidecars by default, which protects traffic inside the platform. Allow outbound access only to the sources and reaction targets. - Management surface. The
drasiCLI talks to the management API with your Kubernetes credentials, so access follows Kubernetes RBAC. Do not expose the Debug reaction or the Result reaction without authentication. Drasi Server’s REST API has no built-in authentication; put it behind a proxy that enforces it. - Supply chain. Pin versions, mirror images, and scan them like any other workload. Custom sources and reactions run code with access to your data, so review them.
Monitoring and day-2 operations
The install includes an OpenTelemetry collector, and every Drasi component runs as a pod, so the first checks are Kubernetes ones: pod status and restarts in drasi-system and in the namespaces where sources and reactions run, and their logs. Beyond that:
drasi list source,drasi list queryanddrasi list reactionshow the status of each resource, anddrasi waitblocks until a resource is ready, which suits pipelines.- The Result reaction exposes a query’s current result set over REST, which is a quick way to compare what Drasi thinks the answer is with what the database says.
- Watch source lag: for PostgreSQL, the replication slot’s confirmed position against the current WAL position.
- Watch query and reaction health: restarts, error logs and the delay between a source change and the reaction’s output.
- Watch duplicate delivery in your consumers, which tells you how often at-least-once delivery matters.
- Capacity follows the number of queries and the size of their working state, so measure memory per query container as you add queries, and use a second query container to separate heavy queries from light ones.
- Upgrades. Pin the version, read the release notes, and test an upgrade on a copy with real sources, because the platform is young and moves quickly. I could not confirm the current platform release number from the official releases page when writing this, so check the version with the CLI and the releases page before you pin it.
Usage cases
- Operational alerting on state. Detect that a condition has held for a duration (a device too hot, a queue too long) and notify, with the transition into and out of the condition as separate events.
- Cross-system conditions. Combine data from two databases, such as orders and drivers, into one live view without building a pipeline.
- Cache and view maintenance. Push result changes into a store or UI so it updates when its source data changes, with no polling.
- Kubernetes and security checks. Watch cluster resources, for example for a container with a known vulnerability, using the Kubernetes source.
- Feeding AI agents. An MCP reaction exposes query results to an agent so it reacts to changes rather than polling.
In the Sprint 4 final project, a pharmaceutical plant’s platform uses Drasi to turn changes in its stored machine data into alarm notifications.
Version and project status
Drasi is a CNCF Sandbox project, accepted on January 21, 2025, developed in the drasi-project organisation on GitHub. The main repositories are the platform (drasi-platform), the core engine (drasi-core), Drasi Server and the documentation. The engine has been extended since launch: GQL support arrived alongside openCypher, the embeddable drasi-lib library reached 0.9.x on crates.io with Python and Node.js bindings, and a plugin system for sources and reactions publishes plugins as packages in a container registry. Sandbox means the project is early: expect change, and expect some pages of the documentation to describe more than one way to run it. The roadmap pages and the issue tracker are the place to check what is next.
Adoption
The CNCF publishes a case study for Netstar, a fleet and vehicle monitoring company, which describes adopting Drasi as the backbone of its real-time data architecture to reduce repeated custom integrations, using the Drasi plugin for Grafana to consolidate query results, and exploring predictive maintenance as a next step. The launch announcement also described reference scenarios, such as building comfort monitoring and vehicle maintenance, as examples. Few other public adopters are documented, which is normal for a Sandbox project, so evaluate it with your own proof of concept.
Alternatives
| Solution | Model | Best suited to |
|---|---|---|
| Debezium with Kafka | Change data capture into a broker | Moving raw changes between systems at scale, with consumers deciding what matters |
| Apache Flink or Kafka Streams | Stream-processing frameworks | Complex, high-volume event processing where you write and operate the jobs |
| Materialize or RisingWave | Streaming databases with incrementally maintained SQL views | Always-fresh SQL views and dashboards with a SQL-first team |
PostgreSQL triggers with LISTEN/NOTIFY |
Built-in database features | One database, simple conditions, no extra platform |
| Polling with a scheduler | Periodic queries | Low-frequency checks where simplicity wins |
| KEDA | Event-driven autoscaling | Scaling workloads from event sources rather than reacting with arbitrary actions |
Strengths and limitations
| Strengths | Limitations |
|---|---|
| Declarative queries replace polling and hand-written stream jobs | A Sandbox project: young, with fast-moving docs and more than one deployment mode |
| Emits only added, updated and deleted results, including transitions out of a condition | A Cypher and GQL subset: no ORDER BY, LIMIT or DISTINCT, and graph thinking has a learning curve |
| Joins across sources without moving the data first | First-party sources and reactions skew toward Azure and PostgreSQL-family systems; none for MQTT, Kafka or Google Pub/Sub that I could find |
Temporal functions such as trueFor express “for how long” conditions |
Default install brings Dapr, Redis and MongoDB, single instance each |
| Kubernetes-native resources, a CLI that fits pipelines, and an embeddable library | Logical replication needs care with slots, WAL growth and failover |
Recommendation
Assess. Drasi is a promising fit when you have a clear “notify me when this condition on my data becomes true” requirement, your data lives in a supported source, and polling or a custom pipeline is the alternative. Prove it with one source, one continuous query and one durable reaction, measure the replication slot and the end-to-end delay, and make your consumers idempotent. If your changes arrive as MQTT or Kafka messages rather than database rows, or you need the full power of SQL windows and ordering, choose a stream processor. Re-check the supported sources, reactions and query language on the documentation site before committing, because they are the parts that change fastest.
References: Drasi documentation · Drasi platform on GitHub · Drasi on the CNCF project page · Netstar case study · Launch announcement · GQL support in Drasi · drasi-lib on docs.rs · PostgreSQL source plugin · CNCF Landscape entry

