Skip to content
🐋Cetacean

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:

ToWhen
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_check turns 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.mode oidc
Tailscale’s coordination server and relays, and log.tailscale.com
With auth.tailscale.mode tsnet. The embedded node uploads its own diagnostic logs unless Cetacean’s environment sets TS_NO_LOGS_NO_SUPPORT=true
The https:// URL an MCP client names as its client ID
With oauth.enabled and oauth.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

SymptomWhat it means
Every list is empty, and GET /-/ready answers 503
Cetacean can’t read the swarm. Resource endpoints answer 503 with code ENG001, and the log carries one full sync resource failed line 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 with docker info --format '{{.Swarm.ControlAvailable}}', which prints true on 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/json and read the Allow response header: GET, HEAD alone 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/status says 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

PageCovers
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_path for a proxy on a sub-path