One workload per role -- engine as a StatefulSet, worker, ui, notifier and sink as Deployments -- sharing one log server. This is the shape Choose your deployment points at for Kubernetes split by role. Topologies, section 2.2, is the mechanism.

1. Prerequisites

A cluster with a default StorageClass (for the log server's PVC) and helm installed locally.

2. Install

helm install jaque oci://<registry>/jaque/charts/jaque --version <chart version> \
  -f values.yaml

See Kubernetes for what deploymentMode: split renders per role and what the chart refuses without log.url.

3. The log server

Apply deploy/kubernetes/distributed/log.yaml in the same namespace before installing the chart -- a minimal StatefulSet, Service and PVC running the same software values.yaml's log.url points at:

# deploy/kubernetes/distributed/log.yaml
apiVersion: v1
kind: Service
metadata:
  name: jaque-log
spec:
  clusterIP: None
  selector:
    app: jaque-log
  ports:
    - name: log
      port: 4222
      targetPort: 4222
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: jaque-log
spec:
  serviceName: jaque-log
  replicas: 1
  selector:
    matchLabels:
      app: jaque-log
  template:
    metadata:
      labels:
        app: jaque-log
    spec:
      containers:
        - name: log
          image: nats:2.10-alpine
          args: ["-js", "-sd", "/data"]
          ports:
            - name: log
              containerPort: 4222
          volumeMounts:
            - name: data
              mountPath: /data
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 5Gi
kubectl apply -f deploy/kubernetes/distributed/log.yaml

4. The values file

The canonical copy is deploy/kubernetes/distributed/values.yaml in the repository.

# deploy/kubernetes/distributed/values.yaml
deploymentMode: split

image:
  tag: ""

config: |
  hosts: {
  	self: {
  		address: "127.0.0.1"
  		check: {type: "icmp", host: "127.0.0.1"}
  	}
  }

apiToken: ""

log:
  url: "nats://jaque-log:4222"

queue:
  url: ""

engine:
  replicas: 2
  heartbeatTTL: ""

worker:
  enabled: true
  replicas: 2
  zones:
    - default
  netRaw: true

ui:
  enabled: true
  replicas: 1
  ingress:
    enabled: false
    className: ""
    host: ""

sink:
  enabled: true
  replicas: 1
  select: []

notifier:
  enabled: true
  replicas: 1

5. Start

helm install jaque oci://<registry>/jaque/charts/jaque --version <chart version> \
  -f deploy/kubernetes/distributed/values.yaml

6. Verify

kubectl exec deploy/jaque-ui -- /jaque version
kubectl port-forward svc/jaque 8080:8080
curl http://localhost:8080/status.json

The dashboard is at http://localhost:8080/ once the port-forward is up (or through ui.ingress if enabled). For jaque_membership_size, port-forward the engine StatefulSet's Service instead and check /metrics there -- ui never joins engine membership, so its own /metrics reads 0. With engine.replicas: 2, either engine pod reports jaque_membership_size as 2, since both see the same shared membership view.

7. Add a worker zone

Set worker.zones to include the new zone name and give the hosts in your config that zone, or run a second helm install/values.yaml overlay with a different worker.zones if the zone needs its own nodeSelector or tolerations. A check only runs on a worker serving its object's zone; see Topologies, section 3.

8. Where things live

Config is the config value, mounted as a ConfigMap into every role that takes -config. State (the event log) lives on the log server's PVC in log.yaml; no jaque workload keeps its own persistent state. Logs go to kubectl logs deploy/jaque-<role> (or statefulset/jaque-engine).

9. Upgrade

helm upgrade jaque oci://<registry>/jaque/charts/jaque --version <chart version> \
  -f deploy/kubernetes/distributed/values.yaml

Leave image.tag empty so the chart and the binary it deploys stay the same release. Roles upgrade independently as Kubernetes rolls each workload; engine is a StatefulSet with podManagementPolicy: Parallel.

10. Next steps

CUE in practice adds services and contacts to config. Contacts and policies wires a hard state to a person, delivered by the notifier role. Cluster and coordination is what engine.replicas: 3+ buys over 2.

11. Security considerations

worker.netRaw grants NET_RAW only to the worker role's container; no other role's containerSecurityContext carries it. ui.ingress.tls is the chart's only TLS termination; without it, and without an authenticating proxy in front, the dashboard, /metrics and any exposed Livestatus port are unauthenticated. See Kubernetes, section 7, and Security.