docker-compose dashboard · Rust · htmx
DOCK
SPACE
Every compose stack on your machine — live, editable, one click from up or down.
Point it at a folder of stacks and it finds every compose.yaml: live status, services and ports, logs with a real live tail, an editor that validates before it saves, images/volumes/networks with scoped prune, and an activity feed. A single-machine take on the best parts of sencho — themed in the shared CYBERGRID palette.
01 / Features
The whole harbor,
one screen.
Everything refreshes on its own every few seconds, and slow actions never freeze the page — a cold image pull returns "Working…" instantly and the next poll shows the real outcome.
RUNNING · PARTIAL · STOPPED · UNKNOWN
From docker compose ps, with a per-service breakdown. Unknown is its own state — the daemon is unreachable, not "definitely stopped".
SERVICES & PORTS
Parsed straight from each compose file — both short "8081:8081" and long {target, published} syntax.
UP · DOWN · RESTART
Responsive even for slow pulls; a failed action shows a banner on the card instead of silently doing nothing.
SNAPSHOT + LIVE TAIL
Last 200 lines on demand, or a real Server-Sent-Events stream of docker compose logs -f.
COMPOSE DOCTOR
Edit in place; Validate runs docker compose config on your edit before saving, so a typo is an inline error, not a broken file.
RESOURCES & ACTIVITY
Every image, volume and network with a scoped prune, plus a feed of every start/stop/save/prune — failures show the real command output.
02 / Security
Root-grade power,
loopback-tight.
Docker access is effectively root, and Dockspace has no login. So it binds to 127.0.0.1 only — and refuses the two tricks a web page can use to reach a localhost service.
200 GET dashboard200 POST from its own page (same-origin)403 POST from evil.example (cross-site form)403 POST with a foreign Origin403 Host: evil.example (DNS rebinding)200 curl, no browser headers
- Cross-site requests refused: state-changing requests whose
Sec-Fetch-SiteorOriginisn't its own are blocked — no forged "save this compose file, now start it". - DNS rebinding refused: every request must carry a loopback
Hostfor its port. - Least privilege: run it as a hardened service that gets the
dockergroup itself, from a root-owned binary — so your login doesn't need Docker access at all. - Never expose it: no auth by design; reach it locally or over an SSH tunnel.
03 / Setup
Moor
the stacks.
Keep one folder per stack (compose.yaml + .env + config/), point Dockspace at the root, and open the dashboard.
git clone https://github.com/cybercore-tech/dockspace
cd dockspace && cargo build --release
DOCKSPACE_ROOT=~/Containers DOCKSPACE_PORT=7070 \
./target/release/dockspace
# → http://127.0.0.1:7070[Service]
User=youruser
SupplementaryGroups=docker
ExecStart=/usr/local/bin/dockspace
Environment=DOCKSPACE_ROOT=/home/youruser/Containers
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/home/youruser/Containers /home/youruser/.cache/dockspace
CapabilityBoundingSet=04 / Theme matrix
Paint
the hulls.
Every Cybercore palette from the shared schema, grouped by family — plus Dockspace's own signature mashups. Your pick follows you across Cybercore sites.