Skip to content

CLI (dataflow-cli)

Command-line client for DataFlow: talk to the Web GUI API, or run a DataFlow manifest locally without Kubernetes.

When to read this: scripting against the web API, local pipeline smoke tests, or day-2 ops without kubectl YAML edits.

See also: Web GUI · Getting Started · DataFlow Spec

Install

Homebrew

brew install dataflow-operator/tap/dataflow-cli

The formula is published to the dataflow-operator/tap tap with each v* release (macOS and Linux, amd64 and arm64).

Release binary

A v* tag publishes binaries and SHA256SUMS to GitHub Releases.

OS Architecture Asset
Linux amd64 dataflow-cli-linux-amd64
Linux arm64 dataflow-cli-linux-arm64
Linux armv7 dataflow-cli-linux-arm-v7
macOS amd64 dataflow-cli-darwin-amd64
macOS arm64 dataflow-cli-darwin-arm64
Windows amd64 dataflow-cli-windows-amd64.exe
Windows arm64 dataflow-cli-windows-arm64.exe
curl -fsSL -o dataflow-cli \
  "https://github.com/dataflow-operator/dataflow-cli/releases/download/vX.Y.Z/dataflow-cli-linux-amd64"
chmod +x dataflow-cli

Replace vX.Y.Z and the asset name for your OS.

Docker

Images: ghcr.io/dataflow-operator/dataflow-cli (linux/amd64, linux/arm64). :latest and the version tag come from a v* release. :main tracks the main branch.

docker pull ghcr.io/dataflow-operator/dataflow-cli:latest

docker run --rm ghcr.io/dataflow-operator/dataflow-cli:latest --help

# On Linux, --network host reaches a source or sink on the host.
# On Docker Desktop, use host.docker.internal in the manifest instead of localhost.
docker run --rm --network host \
  -v "$PWD:/work" -w /work \
  ghcr.io/dataflow-operator/dataflow-cli:latest \
  run manifest.yaml --once

# SecretRef is read from the mounted kubeconfig.
docker run --rm --network host \
  -v "$HOME/.kube:/root/.kube:ro" \
  -v "$PWD:/work" -w /work \
  ghcr.io/dataflow-operator/dataflow-cli:latest \
  run manifest.yaml --once

Build from the dataflow-cli directory:

docker build -t dataflow-cli .

From source

Go 1.25+:

cd dataflow-cli
go build -o dataflow-cli .

In the monorepo, go.work builds the CLI against the local dataflow checkout. go.mod requires github.com/dataflow-operator/dataflow v1.12.1-0.20261012061201-dec4eee83b3d (branch feat/pkg-run-oneshot) and does not use a replace directive. Tag v1.12.0 does not contain the one-shot pkg/run API. Pushes to main in the dataflow-operator/dataflow-cli repository run semantic-release, which tags vX.Y.Z and updates that requirement to @latest only when the module still builds the CLI. The same workflow stamps the version into the binary (dataflow-cli --version).

Source: dataflow-operator/dataflow-cli. CI: .github/workflows/build.yml.

Global flags

Flag / env Description Default
--api-url / DATAFLOW_API_URL Base URL of the dataflow-web API http://localhost:8080/api
--namespace Kubernetes namespace for DataFlow resources default

Point --api-url at a port-forwarded or Ingress GUI, for example:

kubectl port-forward svc/dataflow-operator-gui 8080:8080 -n dataflow-system
export DATAFLOW_API_URL=http://localhost:8080/api

Web API commands

Same actions as the browser UI:

# Namespaces
dataflow-cli namespaces

# DataFlows
dataflow-cli dataflows list
dataflow-cli dataflows list --namespace=my-ns
dataflow-cli dataflows get my-dataflow
dataflow-cli dataflows create --file=manifest.yaml
dataflow-cli dataflows update my-dataflow --file=manifest.yaml
dataflow-cli dataflows delete my-dataflow

# Logs (--follow / -f for live stream)
dataflow-cli logs my-dataflow --tail=200
dataflow-cli logs my-dataflow -f

# Status and metrics
dataflow-cli status my-dataflow
dataflow-cli metrics my-dataflow

All of these accept --namespace (default default).

Local run (no Kubernetes)

Runs the pipeline in-process inside the CLI. The cluster and operator are not required; source and sink must be reachable (Kafka, PostgreSQL, etc.).

dataflow-cli run manifest.yaml
dataflow-cli run manifest.yaml --log-level=debug

Stop with Ctrl+C.

One-shot migration

--once reads a polling source until it is exhausted, flushes the sink, writes a local checkpoint, and exits. Use it for a snapshot copy (for example PostgreSQL to PostgreSQL) without a cluster. kind: DataFlowCron is accepted; schedule and triggers are ignored.

Supported sources: postgresql, clickhouse, trino, iceberg, nessie. kafka and postgresql-cdc stay open, so --once fails before connecting.

dataflow-cli run manifest.yaml --once
dataflow-cli run manifest.yaml --once --checkpoint-file .dataflow/pg-migration.json --reset
dataflow-cli run manifest.yaml --once --timeout 2h
Flag Description
--once Exit when the source is exhausted
--checkpoint-file Checkpoint JSON. Default for --once: .dataflow/<metadata.name>.json
--reset Clear that source's checkpoint before start
--timeout Maximum run time (2h, 30m). 0 means no limit
--log-level debug, info, warn, error

Ctrl+C or a timeout flushes the checkpoint and exits with a non-zero status. The next run resumes from the file. On success the CLI prints source, sink, processed, errors, and the checkpoint path.

checkpointPersistence: false skips the file. SecretRef is resolved from the current kubeconfig (or in-cluster config) before the pipeline starts. A missing secret or key fails the run before connect.

The manifest stays a normal DataFlow. On the source set changeTrackingColumn, orderByColumn, and readBatchSize. On the sink set upsertMode and conflictKey.

apiVersion: dataflow.dataflow.io/v1
kind: DataFlow
metadata:
  name: pg-migration
spec:
  checkpointSyncOnAck: true
  source:
    type: postgresql
    config:
      connectionString: "postgres://user:pass@localhost:5432/src?sslmode=disable"
      table: public.source_table
      changeTrackingColumn: id
      orderByColumn: id
      readBatchSize: 10000
      pollInterval: 5
  sink:
    type: postgresql
    config:
      connectionString: "postgres://user:pass@localhost:5432/dst?sslmode=disable"
      table: public.target_table
      upsertMode: true
      conflictKey: id
      batchSize: 1000
dataflow-cli run pg-migration.yaml --once --timeout 2h

Credentials in local manifests

Local-dev samples may use plaintext connection strings. Prefer SecretRef for anything shared or production-like. See Best Practices.

Example manifest

Standard DataFlow CR YAML:

apiVersion: dataflow.dataflow.io/v1
kind: DataFlow
metadata:
  name: kafka-to-postgres
spec:
  source:
    type: kafka
    config:
      brokers:
        - localhost:9092
      topic: input-topic
      consumerGroup: dataflow-group
  sink:
    type: postgresql
    config:
      connectionString: "postgres://user:pass@localhost:5432/db?sslmode=disable"
      table: output_table

Golden samples: config/samples (prefer *-secrets.yaml patterns for cluster apply).

When to use CLI vs GUI vs kubectl

Goal Prefer
Browse / edit in the browser Web GUI
Scripts, CI, quick API calls dataflow-cli
Full cluster control, CRD status, events kubectl
AI-assisted YAML draft/validate MCP + Agent Skills