Getting started
Requirements
- A Docker Swarm mode cluster. Single-node swarms work.
- Docker Engine 26.1 or newer, speaking Docker API 1.46 or above.
- A manager node to run Cetacean on. Cetacean reads the swarm API, which only managers serve.
- The Docker socket, mounted read-only into the container.
Run it
Container images are published to ghcr.io/radiergummi/cetacean. Pick one of the following paths, run it on a manager
node, and open http://<manager>:9000.
Single container
docker run -d --name cetacean \
-p 9000:9000 \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-v cetacean_data:/data \
-e CETACEAN_DATA_DIR=/data \
ghcr.io/radiergummi/cetacean:latest
Mounting the socket :ro protects the file, not the API—Cetacean can still scale and restart services.
Keep the cetacean_data volume. It holds the cached cluster state and the failure history behind the
flaky-service recommendation, both of which are lost with the container otherwise.
Stack deployment
compose.yaml deploys the same container as a stack service, pinned to a manager, with resource limits and a
volume for its state. It has no prerequisites:
curl -O https://cetacean.mazetti.me/dist/compose.yaml
docker stack deploy -c compose.yaml cetacean
On a swarm with several managers, pin the task to one of them with node.hostname == <your-manager>, or give it
a volume driver every manager shares. The default node.role == manager constraint lets a reschedule land on a
different node, where it finds an empty volume and loses the cached state.
From source
Building requires Go 1.27+ and Node.js 24+. make build installs the frontend dependencies, builds the dashboard
and the MCP widgets, and compiles the binary, which embeds both.
git clone https://github.com/Radiergummi/cetacean.git
cd cetacean
make build
./cetacean
The binary defaults to unix:///var/run/docker.sock and listens on :9000.
What you’ll see
The dashboard opens on the cluster overview: health cards for nodes, services and failed tasks, a capacity
section, and a feed of recent resource changes. The overview fills in as Cetacean reads the cluster, so give it a
moment on a large swarm. GET /-/ready turns 200 at the same point, which is what to gate a load balancer on.
Charts are missing until you configure Prometheus, and a banner at the top of the page says so.
Add monitoring
Cetacean works without Prometheus. Adding it unlocks CPU and memory charts, resource gauges, capacity bars, and sizing recommendations.
The bundled stack runs Prometheus,
node-exporter and cAdvisor.
It reads prometheus.yml from the directory you deploy it from, so download both:
curl -O https://cetacean.mazetti.me/dist/compose.monitoring.yaml
curl -O https://cetacean.mazetti.me/dist/prometheus.yml
docker stack deploy -c compose.monitoring.yaml monitoring
That creates a monitoring overlay network. compose.prometheus.yaml is a small overlay that joins Cetacean to
it and sets the Prometheus URL; deploy it on top of compose.yaml:
curl -O https://cetacean.mazetti.me/dist/compose.prometheus.yaml
docker stack deploy -c compose.yaml -c compose.prometheus.yaml cetacean
Keep passing both files on every later redeploy. Deploying compose.yaml alone drops the network and the URL
again, and the charts go empty.
See Monitoring for what Cetacean needs from the scrape if you already run Prometheus, and for the detection banner that tells you which exporter is missing.
Add authentication
By default anyone who can reach Cetacean can read everything, so do this before exposing it beyond a trusted
network. Set auth.mode to oidc, tailscale, cert, or headers; see
Authentication for the settings each mode needs. TLS termination is available in any mode via
tls.cert and tls.key.
Independently of who is signed in, server.operations_level caps what Cetacean may
change at all. It’s 0—read-only—until you raise it, so a deployment that writes anything says so on
purpose. Set it to 1 for operational writes (scale, update image, roll back, restart).
Outbound connections
Cetacean sends no telemetry, and the dashboard loads nothing from a third party: its content security policy allows its own origin only. The server opens these connections, and no others:
- The Docker daemon at
docker.host - Always
api.github.com, for the latest Docker Engine release- When a node’s page opens, or anyone requests
/-/docker-latest-version, which needs no credentials. The answer is cached for an hour.server.version_checkturns it off; off or blocked, the node page shows no update hint - Prometheus at
prometheus.url - When set
- The OIDC issuer, for discovery, signing keys, and the code exchange
- With
auth.modeoidc - Tailscale’s coordination server and relays, and
log.tailscale.com - With
auth.tailscale.modetsnet. The embedded node uploads its own diagnostic logs unless Cetacean’s environment setsTS_NO_LOGS_NO_SUPPORT=true - The
https://URL an MCP client names as its client ID - With
oauth.enabledandoauth.cimd_enabled, when a client signs in that way. Private and loopback addresses are refused - The OpenTelemetry collector at
tracing.endpoint - When set, with
mcp.enabled
In local Tailscale mode, Cetacean talks only to the host’s Tailscale daemon, which makes its own connections.
When something is wrong
- Every list is empty, and
GET /-/readyanswers503 - Cetacean can’t read the swarm. Resource endpoints answer
503with codeENG001, and the log carries onefull sync resource failedline per resource with the Docker error on it. Either the socket isn’t mounted or not readable, or the node is a worker—only managers serve the swarm API. Check withdocker info --format '{{.Swarm.ControlAvailable}}', which printstrueon a manager - Cetacean exits at startup with
bind: address already in use - Something else holds the port. Move it with
server.listen_addr - Action buttons are missing from a detail page
- Your operations level or your grants don’t allow that write. Fetch the resource with
Accept: application/jsonand read theAllowresponse header:GET, HEADalone means no write is available to you - Charts are empty and a banner says so
- Prometheus is unset, unreachable, or an exporter isn’t reporting.
GET /metrics/statussays which; see Monitoring - Restarting loses the recent-failure history
- The data directory isn’t on a volume—see the note under Stack deployment
GET /-/health answers 200 whenever the process is up, including while Cetacean can’t reach Docker.
/-/ready is the one that tracks whether it has actually read the cluster.
Where to go next
- Dashboard
- Navigation, the command palette, and the chart and log viewer controls
- Monitoring
- Prometheus, node-exporter, and cAdvisor, for charts and capacity bars
- Recommendations
- What Cetacean flags about your cluster, and how to tune or turn it off
- Integrations
- Structured panels for Traefik, Shepherd, swarm-cronjob, and Diun
- Authorization
- Per-resource read and write grants, once callers are identified
- MCP server
- The same access for an AI agent, under the same operations level and grants
- API guide
- REST endpoints, SSE streams, and the Atom feed on every resource page
- Configuration
- Every setting, including
server.base_pathfor a proxy on a sub-path