Monedula
monedula-gitops docs menu

Get started with the CLI

Install monedula-gitops, point it at a local Kafka cluster, and create your first topic with producer and consumer access, from diff to verify.

Tutorials 1 of 2

5 min read

Updated 5 October 2026

Markdown

On this page

The local stack authenticates clients with SASL/SCRAM-SHA-512 and protects Schema Registry with HTTP basic auth, the same authentication paths a real deployment uses.

What you need

  • Docker with the Compose plugin. Check with docker compose version.
  • Go 1.26 or newer, to build the CLI. Check with go version.
  • Git and a bash shell. The commands on this page use bash syntax.

1. Get the quickstart files

Clone the repository and change into the CLI quickstart directory:

git clone https://github.com/monedula-dev/monedula-gitops.git
cd monedula-gitops/quickstart/cli

Run every command in this tutorial from this directory. It contains:

quickstart/cli/
  docker-compose.yml          # Kafka (SCRAM) + kafka-init + Schema Registry
  .env.example                # credentials/endpoints (copy to .env)
  clusters/dev.yaml           # KafkaCluster "local" (env-based secret refs)
  manifests/
    payments-orders.yaml      # KafkaTopic payments.orders (+ access + AVRO schema)
    billing-access.yaml       # KafkaAccessPolicy billing-access
    schemas/orders-value.avsc # AVRO value schema (referenced by the topic)

2. Configure the credentials

Copy the example environment file:

cp .env.example .env

The defaults in .env work as they are:

KAFKA_BOOTSTRAP=localhost:9092
KAFKA_USERNAME=admin
KAFKA_PASSWORD=admin-secret
SCHEMA_REGISTRY_URL=http://localhost:8081
SR_USERNAME=sr
SR_PASSWORD=sr-secret

KAFKA_USERNAME and KAFKA_PASSWORD are the SCRAM-SHA-512 super-user, and SR_USERNAME and SR_PASSWORD the Schema Registry basic-auth user.

The CLI connects as the super-user so that it bypasses ACL enforcement: a principal without super-user rights can lock itself out once apply creates a topic’s ACLs, as Connect to a secured Kafka cluster explains.

3. Start Kafka and Schema Registry

docker compose up -d

Wait for the services to become healthy:

docker compose ps

The kafka-init container creates the SCRAM users and must finish before Kafka accepts those credentials. Schema Registry starts only after kafka-init succeeds and Kafka is healthy. Follow their logs until things settle:

docker compose logs -f kafka-init schema-registry

kafka-init should exit 0, and schema-registry should log that it is listening on 0.0.0.0:8081. Press Ctrl-C to stop following the logs.

4. Load the credentials into your shell

clusters/dev.yaml tells the CLI to read the credentials from environment variables. Load .env into the current shell:

set -a; source .env; set +a

Run the remaining steps in this same shell.

5. Build the CLI

Build the binary from the cloned source:

go build -o ../../bin/monedula-gitops ../../cmd/monedula-gitops

Put the bin directory on your PATH for this shell and check that the binary runs:

export PATH="$PWD/../../bin:$PATH"
monedula-gitops version

A build from source reports its version as dev.

6. Read the manifests

The cluster config, clusters/dev.yaml, says where Kafka and Schema Registry are and which environment variables hold the credentials:

apiVersion: gitops.monedula.dev/v1alpha1
kind: KafkaCluster
metadata:
  name: local
spec:
  bootstrapServers: localhost:9092
  auth:
    mechanism: SCRAM-SHA-512
    scram:
      username:
        valueFrom:
          env: KAFKA_USERNAME
      password:
        valueFrom:
          env: KAFKA_PASSWORD
  schemaRegistry:
    endpoint: http://localhost:8081
    auth:
      type: basic
      username:
        valueFrom:
          env: SR_USERNAME
      password:
        valueFrom:
          env: SR_PASSWORD

The topic manifest, manifests/payments-orders.yaml, declares the topic, its configuration, its access and its schema:

apiVersion: gitops.monedula.dev/v1alpha1
kind: KafkaTopic
metadata:
  name: orders
  namespace: payments
spec:
  clusterRef:
    name: local
  topicName: payments.orders
  partitions: 6
  config:
    retention.ms: "604800000"
  access:
    producers:
      - principal: "User:svc-checkout"
        operations:
          - Write
    consumers:
      - principal: "User:svc-fraud"
        group: fraud-orders
        topicOperations:
          - Read
        groupOperations:
          - Read
  schema:
    format: AVRO
    compatibility: BACKWARD
    valueSchema:
      valueFrom:
        file: ./schemas/orders-value.avsc
  reconciliation:
    mode: Enforce
  deletionPolicy: Orphan

clusterRef.name: local points at the KafkaCluster named local in clusters/dev.yaml. The access block becomes Kafka ACLs that let User:svc-checkout write to the topic and User:svc-fraud read it with the consumer group fraud-orders. The schema file path is resolved relative to the manifest.

The third file, manifests/billing-access.yaml, is a KafkaAccessPolicy. It grants User:svc-billing Read, Write and Describe on every topic whose name starts with the prefix billing., which a topic’s access block cannot express.

7. Validate the manifests

monedula-gitops validate -f manifests --cluster-config-file clusters/dev.yaml

validate parses the manifests, resolves the referenced Avro schema file and checks that every clusterRef names a cluster in the config, without contacting Kafka. It reports the resources as valid and exits 0.

8. Preview the changes

monedula-gitops apply -f manifests --cluster-config-file clusters/dev.yaml --dry-run

--dry-run lists the planned operations without changing anything: create operations for the topic, the schema and the ACLs.

9. Apply

monedula-gitops apply -f manifests --cluster-config-file clusters/dev.yaml

This creates the payments.orders topic, registers its Avro value schema and creates the ACLs.

10. Verify and diff

Check that the live cluster now matches the manifests:

monedula-gitops verify -f manifests --cluster-config-file clusters/dev.yaml

verify reports no drift and exits 0. On drift it would exit 1, which makes it a CI check.

Then show the difference between the live state and the manifests:

monedula-gitops diff -f manifests --cluster-config-file clusters/dev.yaml

Right after a clean apply, diff shows no pending operations. diff exits 0 with or without drift and 2 on error, so use verify when the exit code should reflect drift.

11. Look at the result in Kafka

The broker container has the Kafka tools and a client config with the SASL/SCRAM settings. Describe the topic that apply created:

docker compose exec kafka kafka-topics \
  --bootstrap-server localhost:9092 \
  --command-config /etc/kafka/secrets/client.properties \
  --describe --topic payments.orders

List the ACLs:

docker compose exec kafka kafka-acls \
  --bootstrap-server localhost:9092 \
  --command-config /etc/kafka/secrets/client.properties --list

The topic has 6 partitions, and the ACL list contains the grants for User:svc-checkout, User:svc-fraud and User:svc-billing from your two manifests.

12. Import the cluster back into manifests

import cluster works in the other direction: it reads the live cluster and writes manifests. It never changes Kafka.

monedula-gitops import cluster --cluster-config-file clusters/dev.yaml --output-dir ./imported
ls -R ./imported

The KafkaTopic and KafkaAccessPolicy manifests under ./imported mirror what you applied. Import an existing Kafka cluster uses this to bring an existing cluster under management.

13. Try a change that needs approval

A partition increase cannot be undone, so apply holds it back until you approve it.

  1. In manifests/payments-orders.yaml, change partitions: 6 to partitions: 12.

  2. Apply without approval:

    monedula-gitops apply -f manifests --cluster-config-file clusters/dev.yaml

    The partition increase is reported as Blocked and the command exits 3: the plan is sound, it waits for your approval, and nothing changes.

  3. Apply again with the approval flag:

    monedula-gitops apply -f manifests --cluster-config-file clusters/dev.yaml --allow-destructive

    This time the topic grows to 12 partitions.

Change the file back to partitions: 6 before you run the tutorial again on a fresh stack.

14. Clean up

Stop the stack and remove its volumes:

docker compose down -v

Next steps