Docker Compose (secured)
This guide runs a secured Zaris cluster in containers — four nodes and a manager, with mutual TLS on the data plane and token authentication. It uses the attach deployment model and CA mode 3 (pre-issued leaves), in which each node's certificate is mounted into its container rather than issued by the cluster.
It corresponds to the secured sample under docker/ in the Zaris repository — docker/docker-compose.demostore.secure.yml, its store config docker/config/demostore-secure.json, and the PKI you populate under docker/secrets/.
Topology
The manager observes the nodes; an external one-shot container binds the workspace and registers the store. Nodes authenticate to each other with mutual TLS.
- Manager (
clustron-zaris-machine:local) — the control plane. It mounts its security material: the signing key, cluster CA, admin user, and RBAC policy. - Four nodes (
clustron-zaris-node:local) — each mounts its own leaf certificate (node-<i>.pfx) and the shared store config. - Attach one-shot — a short-lived container that logs in, binds the workspace in attach mode, and registers the store as TLS-enabled.
Ports are published so you can reach the Console and Management API from the host — for example, the Console on 18101 and the Management API on 17801. The node client ports are published for in-network clients.
Prerequisites
Complete these before you bring the cluster up.
-
Install Docker and Docker Compose.
-
Build the two images locally from the Zaris repository's
docker/directory:docker build -t clustron-zaris-machine:local -f docker/Dockerfile.machine .
docker build -t clustron-zaris-node:local -f docker/Dockerfile.node . -
Populate the
secrets/directory with the cluster's PKI. Because this is CA mode 3, you provide the certificates — the cluster does not mint them. The sample'ssecrets/README.mdgives a one-time recipe to generate a matched set: bootstrap an open cluster, enable security, harvest the CA and per-node leaves, then stamp the CA into the store config.
By design, a mode-3 secured cluster does not mint its own certificates — an external step issues them. The sample documents that step; in production, your PKI or cert-manager produces the mounted certificates.
What gets mounted
The security of the cluster depends on these mounts landing in the right place. Each row must be present before the containers start, or nodes will fail their TLS handshake.
| Path (on host) | Mounted into | Contents |
|---|---|---|
secrets/manager-security/ | demostore-manager:/var/lib/clustron/security | Token signing key, cluster CA, admin user, RBAC policy, enabled.flag |
secrets/demostore-n<i>.pfx | demostore-n<i>:/var/lib/clustron/certs/demostore-n<i>.pfx | That node's pre-issued leaf certificate (and key) |
config/demostore-secure.json | every node, as /config/clustron.json (read-only) | The store config: security.tls.mode: MutualTls and the trust anchor PEM |
The store config declares the trust model explicitly under security.tls: enabled: true, mode: "MutualTls", and the cluster CA under trustAnchors[].pem. Each node presents its own mounted leaf (demostore-n<i>.pfx) rather than a certificate minted by the cluster — this is exactly the Mode 3 configuration.
Bring the cluster up
-
Start the stack:
docker compose -f docker-compose.demostore.secure.yml -p clustron-demostore-secure up -dThe manager and nodes come up healthy, the attach one-shot binds the workspace and registers the store, and the cluster forms with mutual TLS between nodes.
-
Verify the cluster is stable by querying the membership diagnostics through the published Management API port. Because the cluster is secured, the Management API requires authentication — log in first to obtain an admin token, then pass it as a bearer token:
# Log in and capture an admin token
TOK=$(curl -s -X POST http://localhost:17801/security/login \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"admin"}' \
| grep -oE '"token":"[^"]*"' | head -1 | cut -d'"' -f4)
# Query membership with the token
curl -s http://localhost:17801/admin/v1/stores/demostore/diagnostics/membership \
-H "Authorization: Bearer $TOK"
# → "clusterState":"Stable", "stableMembership":true, "knownMembers":4Without the
Authorizationheader the API returns{"error":"auth_required"}— that is the secured cluster rejecting an unauthenticated call, not a failure of the cluster.A healthy cluster reports
clusterState: Stable,stableMembership: true, andknownMembers: 4. IfknownMembersis below 4, a node failed to join — check that itsdemostore-n<i>.pfxis mounted and that the CA indemostore-secure.jsonmatches the certificates. -
Open the Console at
http://localhost:18101to see the nodes, partitions, and the Encryption (TLS) status.
Connect a client from inside the network
The cluster advertises in-network hostnames, so connect from inside the Docker network — the stack includes a demostore-client box for exactly this. The data plane is both TLS-encrypted and token-authenticated, so a bare zaris:// connect is refused: the connection string must use the zariss:// (TLS) scheme, supply the cluster CA with ca=file:/tmp/ca.pem so the client can validate the node certificates, and carry a token with token=.
First, copy the cluster CA into the client box (the client container does not mount secrets/):
docker cp secrets/ca.pem demostore-client:/tmp/ca.pem
docker exec -it demostore-client pwsh
Then, inside that PowerShell session, log in for a token and connect over TLS:
# Log in to the manager (in-network) and capture an admin token
$tok = (Invoke-RestMethod -Method Post -Uri http://demostore-manager:7801/security/login `
-ContentType application/json `
-Body '{"username":"admin","password":"admin"}').token
Connect-ZrStore -ConnectionString "zariss://demostore-n0:7861,demostore-n1:7861,demostore-n2:7861,demostore-n3:7861/demostore?token=$tok&ca=file:/tmp/ca.pem"
Set-ZrItem -Key "hello" -Value "from docker"
Get-ZrItem -Key "hello"
The node service names are demostore-n0..demostore-n3 (client port 7861); the manager is demostore-manager. See Connecting a secured client for the full client contract (CA pinning, existing tokens, troubleshooting).
A client on the host can reach a seed node but may not resolve the other nodes' in-network hostnames. For host or external clients, publish and advertise host-reachable addresses, or run the client inside the Compose network. This is a networking concern of your deployment, not a limitation of Zaris.
Tear the cluster down
docker compose -f docker-compose.demostore.secure.yml -p clustron-demostore-secure down -v
Store data is in-memory, so down clears it. The mounted secrets/ are untouched — reuse them on the next up, or regenerate them for a fresh cluster.
Next steps
- The trust model behind the mounted certificates → CA and trust modes.
- The other model, where Zaris manages the nodes → Deployment overview (supervisor).
- Connect securely from anywhere → Connecting a secured client.