> Section: [7. Running it](https://jaque.sh/docs/deployment/choose.md)
> Next: deployment/overview
> Index: https://jaque.sh/llms.txt


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](https://jaque.sh/docs/deployment/choose.md) points at for
Kubernetes split by role. [Topologies](https://jaque.sh/docs/deployment/topologies.md), 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

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

See [Kubernetes](https://jaque.sh/docs/deployment/kubernetes.md) 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:

```yaml
# 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
```

```sh
kubectl apply -f deploy/kubernetes/distributed/log.yaml
```

## 4. The values file

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

```yaml
# 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

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

## 6. Verify

```sh
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](https://jaque.sh/docs/deployment/topologies.md), 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

```sh
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](https://jaque.sh/docs/config/cue-in-practice.md) adds services and
contacts to `config`. [Contacts and
policies](../../notifications/contacts-and-policies.md) wires a hard
state to a person, delivered by the `notifier` role. [Cluster and
coordination](../cluster-and-coordination.md) 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](https://jaque.sh/docs/deployment/kubernetes.md), section 7, and
[Security](https://jaque.sh/docs/deployment/security.md).
