The croit Central Dashboard gives you a centralized view of all your Ceph installations worldwide.
151
One screen for all your storage clusters.
If you run more than one Ceph or croit cluster, you log into each one separately to answer simple questions: is everything healthy, how full are we, what changed last night, which disk is about to fail. The Croit Central Dashboard (CCD) answers those questions once, for the whole fleet, and lets you jump into a single cluster when you want the detail.
CCD runs as one container next to your clusters. It reads them through their official API and never changes anything on a cluster. Adding CCD cannot break a running cluster, and it needs no agent installed anywhere.
docker run -d --name ccd -p 8234:8234 -v ccd-data:/data \
-e CCD_PUBLIC_URL=http://localhost:8234 \
-e CCD_SECRET_KEY="$(openssl rand -hex 32)" \
-e OIDC_DISCOVERY_URL=https://id.example.com/application/o/ccd/.well-known/openid-configuration \
-e OIDC_CLIENT_ID=ccd \
-e OIDC_CLIENT_SECRET=... \
croit/ccd:latest
CCD signs in through your own identity provider, so those four OIDC_* values are
required. Setting up single sign-on walks through it
with authentik as the worked example.
The fleet at a glance. Total capacity and how much is used, read and write throughput, IOPS, servers and OSDs online, and how many clusters are healthy, degraded or unreachable. One tile per cluster, worst first, so a problem finds you instead of the other way round.
Aggregated numbers you can trust. A cluster that is unreachable never quietly contributes a zero. Every fleet total says how many clusters it covers, so a suspiciously low number explains itself.
Capacity planning that is worth acting on. A fill history per cluster and a forecast of how many days remain until the near-full and full thresholds, so hardware gets ordered before it is urgent. Ceph fills to its fullest OSD, not to the average, and CCD tracks the fullest OSD for exactly that reason.
Findings from every cluster in one list, most severe first: health warnings, recommendations, a disk whose SMART report went bad, an OSD that is down, a version that has fallen behind the rest of the fleet. Mute one you have consciously accepted and it stops nagging.
Fleet-wide lists you can search and sort across all clusters at once: servers, services and gateways, OSDs, pools, physical disks with their SMART health, and recent tasks. No more opening five tabs to find which host has the failing disk.
Logs from every cluster in one viewer. Follow them live, or walk an absolute time range. Pick a single OSD or host from a table and the viewer narrows to it.
Charts for latency, IOPS, throughput, recovery, placement-group and OSD states, per cluster or summed across the fleet, plus per-OSD latency inside one cluster, which is how you find the one slow disk dragging a pool down.
A world map with a marker per cluster, coloured by health. Useful when clusters sit in different data centres, countries or customers.
One audit trail covering what happened in CCD and on the clusters, so "who changed that" has a single answer.
Single sign-on, and read-only by default. CCD has no user accounts of its own. Your existing logins and groups keep working, and anyone who can log in gets read-only access unless you grant them more.
croit clusters are fully supported and get every feature above.
Plain Ceph clusters connect through the Ceph Manager dashboard API. You get health, capacity, throughput, servers, services, OSDs, pools, the cluster log, the audit trail and Ceph's own health checks. Some things depend on the cluster: disk inventory and SMART need the cephadm orchestrator, and there are no charts, because the Ceph API serves no time series. The capacity trend still works, because CCD records that itself.
CCD tells you in plain words when a feature is unavailable and why, instead of showing an empty page. It also detects the cluster type from the URL when you add it, so you do not have to know.
| Tag | Use it when |
|---|---|
croit/ccd:latest | You just want the current version. Points at the newest release. This is the right choice for almost everyone. |
croit/ccd:v2608 | You want to stay on one release line and pick up its fixes, without moving to a newer line on your own schedule. |
croit/ccd:v2608.1.0 | You need an exact, unchanging version, for example in a change-controlled environment. |
croit/ccd:nightly | You want to try something that is not released yet. Not for production. |
2608.1.0
│ │ └── patch level of that release
│ └──── the first release of that month
└───────── year 26, month 08
So 2608.1.0 is the first release from August 2026, and 2609.2.0 is the second
one from September 2026. You can tell at a glance how current your installation is.
A number ending in .0 is always a release; :nightly builds carry a higher last
digit and are never published as a version tag.
The version your installation runs is shown at the bottom of every page, so a support request can always name it.
linux/amd64. That is the
only architecture CCD is published for, which covers ordinary servers. On an
ARM machine, including an Apple Silicon Mac, see
the note below.ceph mgr module enable dashboard
ceph dashboard ac-user-create ccd -i password-file read-only
Save this as docker-compose.yml, put your values in ccd.env next to it, and run
docker compose up -d:
services:
ccd:
image: croit/ccd:latest
env_file: ccd.env
ports:
- "8234:8234"
volumes:
- ccd-data:/data
restart: unless-stopped
volumes:
ccd-data:
# ccd.env
CCD_PUBLIC_URL=https://ccd.example.com
CCD_SECRET_KEY=<output of: openssl rand -hex 32>
OIDC_DISCOVERY_URL=https://id.example.com/application/o/ccd/.well-known/openid-configuration
OIDC_CLIENT_ID=ccd
OIDC_CLIENT_SECRET=<from your identity provider>
OIDC_ROLE_MAPPING={"storage-admins":"admin","noc":"viewer"}
ccd.env holds secrets. Keep it readable only by the user running Docker.
CCD is published for linux/amd64 only. On an ARM host, an Apple Silicon Mac being
the usual case, docker pull refuses with:
no matching manifest for linux/arm64/v8 in the manifest list entries
Ask for the amd64 image explicitly and Docker runs it under emulation:
docker run -d --name ccd --platform linux/amd64 -p 8234:8234 ...
In compose, add one line to the service:
services:
ccd:
image: croit/ccd:latest
platform: linux/amd64
Emulation is noticeably slower, which is fine for a look around and not something to run a production installation on. Put CCD on an ordinary amd64 server for that.
Open your CCD_PUBLIC_URL and choose Sign in with SSO. You are sent to your
identity provider and back; there is no separate CCD password.
Two roles:
Everyone who can log in is a viewer unless OIDC_ROLE_MAPPING grants more, so
a new installation cannot be changed by accident.
A new installation is not empty. It comes with four demo clusters in different locations, with moving charts, live logs, health findings, tasks and audit entries. They are built into CCD, so nothing is contacted over the network and there is nothing to set up. It is the fastest way to see whether CCD is for you.
The demo clusters have no cluster interface behind them, so there is no link to open one. Everything else behaves like the real thing.
Delete them in the cluster list when you no longer want them and they stay gone. To
skip them from the start, set CCD_DEMO_CLUSTER=off before the first launch.
Go to Clusters, then Add cluster:
| Field | What to put in |
|---|---|
| Name | How it appears in CCD, for example "Frankfurt production" |
| URL | The cluster's API address, for example https://10.0.0.10 |
| Type | croit or Ceph Manager. Press Detect from URL and CCD works it out. |
| Credential | The read-only token or user from above |
| Location | Optional city, latitude and longitude, for the world map |
The tile turns green within a few seconds and the fleet numbers include it. Nothing else needs configuring: CCD polls each cluster continuously and pushes updates to open browser tabs, so pages refresh themselves.
CCD needs four values from your identity provider, and one redirect URI registered on their side:
<CCD_PUBLIC_URL>/auth/oidc/callback
It has to match exactly, including http versus https and the port. For a first
local test that is http://localhost:8234/auth/oidc/callback.
1. Create the provider. Applications, Providers, Create, OAuth2/OpenID Provider.
| Field | Value |
|---|---|
| Name | CCD |
| Authorization flow | your usual one, for example default-provider-authorization-explicit-consent |
| Client type | Confidential |
| Client ID / Client Secret | authentik generates both; copy them |
| Redirect URIs | https://ccd.example.com/auth/oidc/callback, match set to Strict |
| Signing Key | pick your certificate, so the ID token is signed with RS256 |
| Scopes | leave authentik's defaults for openid, email and profile |
2. Create the application. Applications, Applications, Create. Name it
Croit Central Dashboard, set the slug to ccd, and select the provider from
step 1. The slug is what appears in the discovery URL.
3. Fill in CCD. The discovery URL follows the application slug:
OIDC_DISCOVERY_URL=https://authentik.example.com/application/o/ccd/.well-known/openid-configuration
OIDC_CLIENT_ID=<Client ID from step 1>
OIDC_CLIENT_SECRET=<Client Secret from step 1>
4. Map your groups to roles. Create a group in authentik, for example
storage-admins, and map it:
OIDC_ROLE_MAPPING={"storage-admins":"admin","noc":"viewer"}
CCD reads the group names out of the groups claim. Recent authentik versions
include that claim in the default profile scope, so this usually works with no
further setup.
If your login lands on viewer although your group matches, the claim is not
being sent. Add a scope mapping in Customisation, Property Mappings, Create, Scope
Mapping:
| Field | Value |
|---|---|
| Name | CCD groups |
| Scope name | groups |
| Expression | return [group.name for group in user.ak_groups.all()] |
Add that mapping to the provider's selected scopes, and request it in CCD:
OIDC_SCOPES=openid email profile groups
This step depends on your authentik version, which is why it is written as a fallback rather than a required step.
To see what CCD actually received, log in and read the container log. It reports every sign-in, and the three cases look different on purpose:
[ccd][oidc] martin: claim "groups" [storage-admins] -> roles [admin]
[ccd][oidc] jo: claim "groups" absent -> roles [viewer] (no group matched, used OIDC_DEFAULT_ROLES)
[ccd][oidc] kim: claim "groups" [devs] -> roles [viewer] (no group matched, used OIDC_DEFAULT_ROLES)
absent means your provider is not sending the claim, so fix it there. A claim
that arrives but matches nothing means OIDC_ROLE_MAPPING needs that group name.
The same three things apply everywhere: a confidential client, the redirect URI
above, and a claim carrying group names. If your provider calls that claim
something else, point CCD at it with OIDC_GROUPS_CLAIM, for example roles or
wids.
Everything is set through environment variables. CCD reads them once at start, so restart the container after a change.
| Setting | Required | Meaning |
|---|---|---|
CCD_PUBLIC_URL | yes | The address your users type into the browser, for example https://ccd.example.com. It builds the single sign-on redirect URI, so it must match what your provider allows. |
CCD_SECRET_KEY | yes | 32 random bytes that encrypt stored cluster credentials and sign login sessions. Generate with openssl rand -hex 32. Back it up: lose it and every cluster credential has to be entered again. Changing it logs everyone out. |
CCD_PORT | no | Port inside the container, default 8234. Change the port mapping too if you change this. |
CCD_DB | no | Default sqlite:///data/ccd.db, which is the mounted volume and right for almost everyone. For an external database: postgres://user:password@host:5432/database. |
CCD_DEMO_CLUSTER | no | on by default: registers the four demo clusters while no real cluster is configured. Set to off to skip them. Deleting them in the interface is permanent either way. |
| Setting | Required | Meaning |
|---|---|---|
OIDC_DISCOVERY_URL | yes | Your provider's discovery document, usually ending in /.well-known/openid-configuration. |
OIDC_CLIENT_ID | yes | The client id issued for CCD. |
OIDC_CLIENT_SECRET | yes | The matching client secret. |
OIDC_ROLE_MAPPING | no | Maps a group to a CCD role, as JSON: {"storage-admins":"admin","noc":"viewer"}. Roles are admin and viewer. |
OIDC_DEFAULT_ROLES | no | Roles for a user whose groups match nothing. Default ["viewer"]. |
OIDC_GROUPS_CLAIM | no | Which claim carries the group names, default groups. |
OIDC_SCOPES | no | Requested scopes, default openid email profile. |
OIDC_CLIENT_AUTH | no | basic (default) or post. Change only if your provider requires it. |
A complete docker-compose.yml with every setting in place. Copy it, replace the
five values marked CHANGE ME, and delete the optional lines you do not need:
services:
ccd:
image: croit/ccd:latest
restart: unless-stopped
ports:
# host:container. Change the host side if 8234 is taken; put CCD behind
# your own reverse proxy for TLS.
- "8234:8234"
volumes:
# Cluster list, capacity history, audit trail and settings. Back this up.
- ccd-data:/data
environment:
# ---- required ----------------------------------------------------------
# The address your users open. Must match the redirect URI you registered.
CCD_PUBLIC_URL: https://ccd.example.com # CHANGE ME
# openssl rand -hex 32 -- back this up, see the note below.
CCD_SECRET_KEY: 0000000000000000000000000000000000000000000000000000000000000000 # CHANGE ME
OIDC_DISCOVERY_URL: https://id.example.com/application/o/ccd/.well-known/openid-configuration # CHANGE ME
OIDC_CLIENT_ID: ccd # CHANGE ME
OIDC_CLIENT_SECRET: change-me # CHANGE ME
# ---- optional, shown with their defaults -------------------------------
# Which of your groups may change things. Everyone else is read-only.
OIDC_ROLE_MAPPING: '{"storage-admins":"admin","noc":"viewer"}'
OIDC_DEFAULT_ROLES: '["viewer"]'
OIDC_GROUPS_CLAIM: groups
OIDC_SCOPES: openid email profile
OIDC_CLIENT_AUTH: basic
CCD_PORT: 8234
CCD_DB: sqlite:///data/ccd.db
# Set to off to start without the four built-in demo clusters.
CCD_DEMO_CLUSTER: "on"
volumes:
ccd-data:
Note the quoting: OIDC_ROLE_MAPPING, OIDC_DEFAULT_ROLES and CCD_DEMO_CLUSTER
need single or double quotes in YAML, because {, [ and a bare on mean
something else there.
Prefer to keep secrets out of the compose file? Replace the whole environment:
block with env_file: ccd.env and put the same KEY=value lines in that file,
without quotes.
Update:
docker compose pull && docker compose up -d
Back up the ccd-data volume, mounted at /data. It holds the cluster list,
capacity history, audit trail and settings, so CCD can be recreated from it at any
time. Cluster credentials inside it are encrypted with your CCD_SECRET_KEY, which
therefore belongs in your backup too, stored separately.
Logs: docker logs ccd, or docker compose logs -f ccd.
The container stops right after starting. CCD refuses to run with an incomplete
configuration and names each missing value in its log. Read docker logs ccd. The
usual causes are a missing CCD_SECRET_KEY, a key shorter than 32 characters, or a
mistyped OIDC_* value.
Sign-in fails at the identity provider. The redirect URI does not match. It has
to be exactly <CCD_PUBLIC_URL>/auth/oidc/callback, including scheme and port.
You can log in but cannot add clusters. Your groups mapped to viewer. Add your
group to OIDC_ROLE_MAPPING as admin and restart. If it still happens, the group
claim is not arriving; see the authentik fallback above.
A cluster stays unreachable. The cluster tile shows the reason. Check that the host can reach the URL, that the scheme and port are right, and that the token or user still exists on the cluster. Self-signed certificates are accepted and are never the cause.
A number looks too low. An unreachable cluster contributes nothing rather than a zero, and CCD marks how many clusters each total covers. Look for a cluster that is not currently reachable.
A feature is missing for one cluster. CCD shows what is unavailable and why, usually a Ceph cluster without the cephadm orchestrator, or charts, which plain Ceph cannot serve.
Copyright (c) 2026 croit GmbH. Use of this image is governed by the licence inside
it at /app/LICENSE.md; the third-party components it contains are listed in
/app/NOTICE.md.
Questions, or want CCD to do something it does not do yet? https://croit.io
Content type
Image
Digest
sha256:ecc3d9a1c…
Size
83.5 MB
Last updated
about 2 months ago
docker pull croit/ccd:nightly