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


One container per role -- `engine`, `worker`, `ui`, `notifier`, `sink` --
sharing one log server, all in one stack. This is the shape [Choose your
deployment](../choose.md) points at for Docker Compose split by role.
[Topologies](https://jaque.sh/docs/deployment/topologies.md), section 2.2, is the mechanism.

## 1. Prerequisites

Docker with the compose plugin (`docker compose version`).

## 2. Install

Nothing to install beyond Docker; the stack pulls the jaque image on
first `up`. See [Docker](https://jaque.sh/docs/deployment/docker.md) for what the image is built on
and how every flag maps to a `JAQUE_<FLAG>` environment variable.

## 3. The log server

The `log` service in the stack below is the shared log server every
role points `-eventlog`, `-coordination` and `-queue` at. There is
nothing to run separately; `docker compose up` brings it up with the
rest.

## 4. The files

The canonical copy of both files below is
`deploy/compose/distributed/docker-compose.yml` and
`deploy/compose/distributed/jaque.cue` in the repository. Every role but
`worker` mounts `jaque.cue` read-only; `engine` runs
`-notify-delivery external` because `notifier` is also running.

```yaml
# deploy/compose/distributed/docker-compose.yml
services:
  log:
    image: nats:2.10-alpine
    command: ["-js", "-sd", "/data"]
    volumes:
      - jaque-log-data:/data

  engine:
    image: ${JAQUE_IMAGE:-jaque:latest}
    environment:
      JAQUE_TARGET: engine
      JAQUE_CONFIG: /etc/jaque/config.cue
      JAQUE_LISTEN: 0.0.0.0:8080
      JAQUE_EVENTLOG: nats://log:4222
      JAQUE_COORDINATION: nats://log:4222
      JAQUE_QUEUE: nats://log:4222
      JAQUE_NOTIFY_DELIVERY: external
    volumes:
      - ./jaque.cue:/etc/jaque/config.cue:ro
    depends_on:
      - log
    restart: unless-stopped

  worker:
    image: ${JAQUE_IMAGE:-jaque:latest}
    environment:
      JAQUE_TARGET: worker
      JAQUE_LISTEN: 0.0.0.0:8080
      JAQUE_QUEUE: nats://log:4222
      JAQUE_ZONES: default
    depends_on:
      - log
    restart: unless-stopped

  ui:
    image: ${JAQUE_IMAGE:-jaque:latest}
    environment:
      JAQUE_TARGET: ui
      JAQUE_CONFIG: /etc/jaque/config.cue
      JAQUE_LISTEN: 0.0.0.0:8080
      JAQUE_EVENTLOG: nats://log:4222
      JAQUE_COORDINATION: nats://log:4222
    volumes:
      - ./jaque.cue:/etc/jaque/config.cue:ro
    ports:
      - "8080:8080"
    depends_on:
      - log
    restart: unless-stopped

  notifier:
    image: ${JAQUE_IMAGE:-jaque:latest}
    environment:
      JAQUE_TARGET: notifier
      JAQUE_CONFIG: /etc/jaque/config.cue
      JAQUE_LISTEN: 0.0.0.0:8080
      JAQUE_EVENTLOG: nats://log:4222
      JAQUE_COORDINATION: nats://log:4222
    volumes:
      - ./jaque.cue:/etc/jaque/config.cue:ro
    depends_on:
      - log
    restart: unless-stopped

  sink:
    image: ${JAQUE_IMAGE:-jaque:latest}
    environment:
      JAQUE_TARGET: sink
      JAQUE_CONFIG: /etc/jaque/config.cue
      JAQUE_LISTEN: 0.0.0.0:8080
      JAQUE_EVENTLOG: nats://log:4222
      JAQUE_COORDINATION: nats://log:4222
    volumes:
      - ./jaque.cue:/etc/jaque/config.cue:ro
    depends_on:
      - log
    restart: unless-stopped

volumes:
  jaque-log-data: {}
```

```cue
// deploy/compose/distributed/jaque.cue
hosts: {
	self: {
		address: "127.0.0.1"
		check: {type: "icmp", host: "127.0.0.1"}
	}
}
```

Only `ui` publishes a port; every other role is reached over the compose
network by the other services, the same way a Prometheus scrape config
would reach them.

## 5. Start

```sh
cd deploy/compose/distributed
docker compose up -d
```

## 6. Verify

```sh
docker compose exec ui /jaque version
curl http://localhost:8080/status.json
```

The dashboard is at `http://localhost:8080/`. `jaque_membership_size` is
0 on `ui` -- it never joins engine membership -- so it has to be read
from `engine` instead, which has no published port. Point your metrics
stack at `engine:8080/metrics` on the compose network, the same way it
would reach it in production; with one `engine` replica the value is
`1`.

## 7. Add a worker zone

Add a second `worker` service (or scale the existing one with `docker
compose up -d --scale worker=2`) and set `JAQUE_ZONES` to the zone name
your hosts in `jaque.cue` carry; 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 mounted `jaque.cue`, read-only, on every role but
`worker`. State is the `jaque-log-data` volume, owned by the `log`
service; no other role keeps local state. Logs go to `docker compose
logs <service>`.

## 9. Upgrade

```sh
docker compose pull
docker compose up -d
```

Set `JAQUE_IMAGE` to pin a specific tag instead of `latest`. The event
log on `log`'s volume survives the upgrade.

## 10. Next steps

[CUE in practice](https://jaque.sh/docs/config/cue-in-practice.md) adds services and
contacts to `jaque.cue`.
[Contacts and policies](https://jaque.sh/docs/notifications/contacts-and-policies.md)
wires a hard state to a person, delivered by the `notifier` role.
[Cluster and coordination](https://jaque.sh/docs/deployment/cluster-and-coordination.md) is what
adding a second `engine` service buys.

## 11. Security considerations

Only `ui`'s port is published; every other role stays on the compose
network. [Cluster and coordination](https://jaque.sh/docs/deployment/cluster-and-coordination.md),
section 7, states what access to the log server's subjects amounts to
-- treat that network the same as the dashboard's.
