Skip to content

MonoVertex

A MonoVertex is the simplest way to run Numaflow. It is a single vertex that reads from a Source, optionally transforms or maps the data, and writes to a Sink. Because there is only one vertex and no edges, a MonoVertex needs no Inter-Step Buffer Service.

This page builds up in two steps:

  1. A simple MonoVertex - a built-in source and sink, no custom code.
  2. A MonoVertex with your own code - a user-defined source, transformer, map, and sink.

This page assumes you have already completed Prerequisites & Installation.

A Simple MonoVertex

We start with the smallest possible MonoVertex: a built-in generator source that produces messages, wired directly to a built-in log sink that prints them. No custom containers are involved.

Deploy the MonoVertex

kubectl apply -f https://raw.githubusercontent.com/numaproj/numaflow/main/examples/20-simple-mono-vertex-builtin.yaml

Verify the Deployment

List the deployed MonoVertices:

kubectl get monovertex # or "mvtx" as a short name

You should see an output similar to this:

NAME                         PHASE     DESIRED   CURRENT   READY   AGE   REASON   MESSAGE
simple-mono-vertex-builtin   Running   1         1         1       38s

Inspect the status by listing the pods. Note that the pod names in your environment may differ from the example below:

# Wait for pods to be ready
kubectl get pods

NAME                                                READY   STATUS    RESTARTS   AGE
simple-mono-vertex-builtin-mv-0-w7fmq               2/2     Running   0          2m30s
simple-mono-vertex-builtin-mv-daemon-55bff65db5-x   1/1     Running   0          2m30s

View the Logs

The log sink runs inside the MonoVertex pod's numa container. Follow its logs. Replace xxxxx with the appropriate pod name from the output above:

kubectl logs -f simple-mono-vertex-builtin-mv-0-xxxxx -c numa

You should see the generated messages being printed:

2025/05/09 11:23:38 (sink)  Payload -  {"value":1746789818182898304}  Keys -  [key-0-0]  EventTime -  1746789818182  Headers -    ID -  0-0
2025/05/09 11:23:38 (sink)  Payload -  {"value":1746789818182898304}  Keys -  [key-0-0]  EventTime -  1746789818182  Headers -    ID -  1-0

Access the Numaflow UI

Numaflow includes a built-in user interface for monitoring your workloads. If your local Kubernetes cluster does not include a metrics server by default (e.g., Kind), install it using the following commands:

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
kubectl patch -n kube-system deployment metrics-server --type=json -p '[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}]'

To access the UI, port-forward the Numaflow server:

kubectl -n numaflow-system port-forward deployment/numaflow-server 8443:8443

Visit https://localhost:8443/ to view the UI.

Note: For more details about the UI features and built-in debugging tools, check out the UI section.

Delete the MonoVertex

kubectl delete -f https://raw.githubusercontent.com/numaproj/numaflow/main/examples/20-simple-mono-vertex-builtin.yaml

A MonoVertex with Your Own Code

The built-in source and sink are handy for a first run, but real workloads usually read from your own systems and run your own logic. This next example uses:

A sink can also declare an OnSuccess Sink to route successfully-processed messages to a second destination (for example, an audit or acknowledgement stream). It is optional and not part of this example; for a complete MonoVertex that wires up a fallback sink, an onSuccess sink, and message routing together, see the 23-mono-vertex-bypass.yaml example.

Each of these is just a container image. In this example the map step uses a pre-built map-cat image (it forwards each message unchanged); to run your own logic you swap in your own image built with one of the Numaflow SDKs.

Deploy the MonoVertex

kubectl apply -f https://raw.githubusercontent.com/numaproj/numaflow/main/examples/21-simple-mono-vertex.yaml

Verify the Deployment

kubectl get monovertex # or "mvtx" as a short name

You should see an output similar to this:

NAME                 PHASE     DESIRED   CURRENT   READY   AGE   REASON   MESSAGE
simple-mono-vertex   Running   1         1         1       38s

Inspect the status by listing the pods. Replace xxxxx with the appropriate pod name from your environment:

# Wait for pods to be ready
kubectl get pods

NAME                                           READY   STATUS    RESTARTS   AGE
simple-mono-vertex-mv-0-w7fmq                  5/5     Running   0          2m30s
simple-mono-vertex-mv-daemon-55bff65db5-mk4g2  1/1     Running   0          2m30s

View Pod Details

Each user-defined container runs as its own container in the pod. To confirm all containers (monitor, udsource, transformer, udf, udsink, and numa) are running, describe the pod:

kubectl describe pod simple-mono-vertex-mv-0-xxxxx

Monitor Logs for the Sink Container

To view logs from the udsink container, run the following. Replace xxxxx with the appropriate pod name:

kubectl logs -f simple-mono-vertex-mv-0-xxxxx -c udsink

Because the map only echoes the messages, the output looks the same as the source data:

2025/05/09 11:23:38 (sink)  Payload -  {"value":1746789818182898304}  Keys -  [key-0-0]  EventTime -  1746789818182  Headers -    ID -  0-0
2025/05/09 11:23:38 (sink)  Payload -  {"value":1746789818182898304}  Keys -  [key-0-0]  EventTime -  1746789818182  Headers -    ID -  1-0

Access the Numaflow UI

If you have not already, port-forward the Numaflow server:

kubectl -n numaflow-system port-forward deployment/numaflow-server 8443:8443

Visit https://localhost:8443/ to view the UI. Below is an example of the UI for this MonoVertex:

Simple MonoVertex View

Delete the MonoVertex

kubectl delete -f https://raw.githubusercontent.com/numaproj/numaflow/main/examples/21-simple-mono-vertex.yaml

Additional Notes

The source code for the user-defined containers used in this MonoVertex is available here:

To write your own, start with the SDKs overview.

Next Step

A MonoVertex is ideal when you read from a source, optionally transform, and write to a sink. When you need multiple processing stages, branching, joins, or windowed aggregation, you need a full pipeline. Continue to the Pipeline guide.