Skip to content
RunDeploy

Local cluster

manifests render --env local produces the same Kubernetes shape the GitOps path deploys (workloads, Dapr annotations, Dapr Components) as one YAML stream on stdout, for a cluster on your own machine:

Terminal window
intropy manifests render --env local | kubectl apply -f -

Run it from the workspace that holds the System host (--system selects one in a multi-system workspace). Only YAML is written to stdout; progress, prompts, warnings, and errors go to stderr, and the complete render is buffered and validated before stdout is written, so a failed render cannot apply an incomplete prefix through the pipe.

The command does not inspect the cluster. Having Dapr installed, and the fixture services running (a RabbitMQ broker for pub/sub, for example), is your cluster setup’s responsibility.

A topology port is a name; locally, each name must be told which adapter to resolve to. Without that, the render refuses:

error: local bindings are required for ports: order-extractor-source, order-loader-destination
pass one '--binding <port>=<fixture>' for each; available fixtures: sftp, http, file, blob

Pass one --binding <port>=<fixture> per port for a reproducible, non-interactive render:

Terminal window
intropy manifests render --env local \
--binding order-extractor-source=file \
--binding order-loader-destination=file | kubectl apply -f -

In an interactive terminal a selector asks for any missing choice instead; the selection applies to that render only and is never persisted. There is no saved binding state: in real environments the bindings live in the GitOps repository, and locally they are an argument.

For the order-flow example (one extractor, one loader, both bound to file), the stream contains the workloads the deployment model implies and the Dapr Components that wire them:

  • A Deployment for the loader, with Dapr sidecar annotations (dapr.io/enabled, dapr.io/app-id: order-loader, dapr.io/app-port: "8080") and an OTLP exporter endpoint in its environment.

  • A CronJob for the extractor (extractors wake, pull, publish, and exit) with concurrencyPolicy: Forbid and its own Dapr annotations.

  • A Dapr Component per port, scoped to the component that uses it. The file fixture becomes bindings.localstorage with a rootPath under /data/:

    apiVersion: dapr.io/v1alpha1
    kind: Component
    metadata:
    labels:
    intropy.io/port: order-extractor-source
    name: order-extractor-source
    namespace: order-flow
    scopes:
    - order-extractor
    spec:
    metadata:
    - name: rootPath
    value: /data/order-extractor-source
    type: bindings.localstorage
    version: v1
  • A pub/sub Component shared by publisher and subscriber, pointing at the local fixture broker.

Everything lands in a namespace named after the System (order-flow here); --namespace changes it. Images default to the local convention (local/<component>:dev); override one with --image <component>=<name:tag>, or retag every component with --image :<tag>.

This is not the inner loop. For building and debugging a System, the host’s Aspire run is faster and needs no cluster: F5, mocked platform services, drop folders standing in for ports, the dashboard for traces (see Systems and topology).

Render to a local cluster when the deployed shape is the thing under test: that the workload kinds and sidecar annotations are what you expect, that port Components are scoped correctly, that the System behaves on real Kubernetes with a real broker. From there, the same model, rendered from the same topology by the same templates, is onboarded into the GitOps repository and moved through environments with releases and promotion.

Full flag detail: intropy manifests render.