Skip to content

aiyu-ai/Dockpit

Repository files navigation

Dockpit

Language: English | 中文

Lightweight Docker container management Web UI. Zero external dependencies (Go standard library only), final image ~10MB. Supports single-host and multi-node federated deployment — manage Docker on many machines from one UI.

Go Docker License

Contents


Features

Category Capability
Container management List running containers (name, image, status, port mappings)
Resource monitoring Inline CPU% + memory; detail modal with network I/O, disk I/O, process count; 2s / 3s auto-refresh
Log viewer Pull by line count (50 / 100 / 200 / 500), SSE live streaming, ANSI escape codes stripped
Remote restart Two-click confirmation to prevent misclicks
Port suggestions Docker API + TCP bind dual-check, click to copy, "refresh" for next batch
Multi-node Hub + Agent architecture, one UI for many machines
Agent discovery Agents register themselves; 30s heartbeat, 90s timeout → marked offline
Security Bearer token auth, Hub ↔ Agent communication (recommend HTTPS reverse proxy)

Architecture

Single host (MODE=both)

┌──────────────────────────────────────┐
│  Dockpit (MODE=both)                 │
│  ┌──────┐  ┌───────────┐  ┌────────┐ │
│  │  UI  │  │ Registry  │  │ Local  │ │
│  │      │→ │ + Proxy   │→ │ Agent  │ │ → /var/run/docker.sock
│  └──────┘  └───────────┘  └────────┘ │
└──────────────────────────────────────┘
                    ↑
                [Browser]

Multi-node (Hub + Agents)

                    ┌─────────────────┐
                    │  Browser (UI)    │
                    └────────┬────────┘
                             ▼
                ┌────────────────────────┐
                │  Hub (MODE=hub/both)   │
                │  ┌──────────────────┐  │
                │  │  Agent Registry  │  │
                │  │  + Reverse Proxy │  │
                │  └──────────────────┘  │
                └──────┬─────────────────┘
                       │ Bearer Token
          ┌────────────┼────────────┐
          ▼            ▼            ▼
     ┌─────────┐  ┌─────────┐  ┌─────────┐
     │ Agent A │  │ Agent B │  │ Agent C │
     │ Docker  │  │ Docker  │  │ Docker  │
     └─────────┘  └─────────┘  └─────────┘

Communication model:

  • On startup, agents call POST /api/agents/register on the hub
  • Every 30s, agents send POST /api/agents/heartbeat
  • If the hub doesn't hear from an agent for 90s → marked offline (kept in registry, can reconnect)
  • The hub routes browser requests by ?agent=<name>:
    • Local agent → in-process call (no HTTP overhead)
    • Remote agent → reverse proxy + inject bearer token
  • SSE log streams use FlushInterval: -1 for realtime delivery

Quick Start

Prerequisites

  • Docker Engine 20.10+ / Docker Desktop
  • One exposed port (default 8080)
  • Access to /var/run/docker.sock

Clone

git clone <repo-url>
cd Dockpit

Single-host deploy (30s)

docker compose up -d --build

Open http://localhost:8080. No extra configuration required.


Deployment Scenarios

Scenario A: Single host

docker compose up -d --build

Open http://localhost:8080.

Scenario B: Central hub + remote agents

Manage Docker on multiple machines from one central UI.

Step 1 — generate a shared token

openssl rand -hex 32

All agents must use the same token. Leaking it grants control over every managed host.

Step 2 — hub machine

cd Dockpit

cat > .env <<EOF
TOKEN=<the-hex-token-you-generated>
AGENT_URL=http://hub.example.com:8080
EOF

docker compose up -d --build

Step 3 — on each remote host

cd Dockpit
cp .env.example .env
vi .env   # fill in values

Example .env:

TOKEN=<same-token-as-hub>
HUB_URL=http://hub.example.com:8080
AGENT_NAME=prod-server-1
AGENT_URL=http://prod-server-1.example.com:8080

Start:

docker compose -f docker-compose.agent.yml up -d --build

Step 4 — verify

Open the hub UI. The agent switcher (top right) should show central plus each remote agent. Within 30s the first heartbeat lands and the green dot turns on.

Scenario C: local test with Go binary

go build -o /tmp/pc .

# Terminal 1: hub on :8080
MODE=both TOKEN=test AGENT_NAME=central \
  AGENT_URL=http://localhost:8080 /tmp/pc

# Terminal 2: simulated remote agent on :8099
MODE=agent TOKEN=test HUB_URL=http://localhost:8080 \
  AGENT_NAME=remote1 AGENT_URL=http://localhost:8099 \
  LISTEN=:8099 /tmp/pc

Open http://localhost:8080 — the agent switcher shows both nodes.


Configuration

All config is via environment variables. docker-compose.yml reads from .env.

General

Variable Default Description
MODE both hub / agent / both
LISTEN :8080 Listen address
TZ Asia/Shanghai Container timezone

Authentication

Variable Required Description
TOKEN hub/agent ✅; both optional Shared secret (Bearer token). Must match across all nodes. In MODE=both a random value is generated if unset (internal use only).

Agent identity

Variable Required Default Description
AGENT_NAME agent ✅ container hostname Display name in the UI; unique within the cluster
AGENT_URL agent ✅ http://localhost:LISTEN (both) URL the hub uses to pull back from this agent — must be reachable from the hub
HUB_URL agent ✅ http://localhost:LISTEN (both) Hub base URL for registration and heartbeats

Resource limits (compose)

  • mem_limit: 512m
  • restart: unless-stopped
  • logging: json-file with 10MB × 3 rotation

MODE=both defaults

When running with MODE=both and no config:

  • TOKEN → random 32-char hex (internal only)
  • AGENT_NAME → container hostname
  • HUB_URLhttp://localhost:8080
  • AGENT_URLhttp://localhost:8080

API Reference

Agent management (hub only)

GET /api/agents

Lists all registered agents. Public (used by the UI).

curl http://hub:8080/api/agents | jq

POST /api/agents/register

Called by agents on startup. Requires bearer token.

curl -X POST http://hub:8080/api/agents/register \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"prod-1","url":"http://prod-1:8080","version":"2.0.0"}'

POST /api/agents/heartbeat

Called by agents every 30s. Requires bearer token.

DELETE /api/agents/remove?name=X

Manually remove an agent. Requires bearer token.

Business API (hub proxies / agents serve)

All business endpoints on the hub accept ?agent=<name>. On an agent, endpoints require bearer token.

Method Path Description
GET /api/containers List running containers
GET /api/containers/stats Stats snapshot for all containers (concurrent)
GET /api/containers/stats/one?id=<cid> Single container detail
GET /api/containers/logs?id=<cid>&tail=100 Recent logs
GET /api/containers/logs/stream?id=<cid>&tail=100 SSE log stream
POST /api/containers/restart {id:<cid>} Restart container
GET /api/ports/suggest?start=8000&count=8 Suggest available ports
GET /api/health Health check (always open)

Example: fetch containers on remote1 via the hub

curl http://hub:8080/api/containers?agent=remote1

UI Guide

Agent switcher

  • Click 🖥 central ▼ at the top
  • Green dot = online, grey dot = offline (no heartbeat for 90s)
  • Click any agent to switch views — all data reloads
  • Online status refreshes every 15s

Port suggestions

  • 8 available ports starting from 8000 by default
  • Click a port → copied to clipboard, chip flashes green for 1.2s
  • "Refresh" continues scanning from the last end

Container table

Each row shows status / name + image / CPU% / memory / port mappings / actions.

  • CPU / memory: mini progress bar + percentage. < 50% green, 50–80% orange, > 80% red. 3s refresh.
  • Port mappings: badges in host:container/proto format

Action buttons

Button Behavior
📊 Monitor Opens resource modal, 2s refresh
📋 Logs Opens log modal, toggle line count, ▶ live stream
⟳ Restart First click turns red "confirm?"; second click within 3s executes

Log modal

  • Select last 50 / 100 / 200 / 500 lines at the top
  • ▶ Live switches to SSE stream (auto-scroll unless user scrolls up)
  • ⏸ Stop closes the stream, keeps current output
  • ANSI escape codes stripped automatically
  • Close with Esc or click outside

Resource modal

Four metric cards:

  • CPU percentage + online CPU count + progress bar
  • Memory percentage + used/limit bytes + progress bar
  • Network I/O cumulative ↓ rx / ↑ tx bytes
  • Disk I/O cumulative read / write bytes
  • Processes current PID count

Refreshes every 2s; interval cleared on close.


Operations

Logs

# Hub
docker compose logs -f dockpit

# Remote agent
docker compose -f docker-compose.agent.yml logs -f dockpit-agent

Restart / upgrade

docker compose restart
# or
docker compose down && docker compose up -d

# Upgrade
git pull
docker compose up -d --build

Rotate token

  1. Stop hub: docker compose down
  2. Stop every agent
  3. Edit TOKEN in all .env files (must stay in sync)
  4. Bring hub up first, then agents

View registry state

curl -s http://hub:8080/api/agents | jq '.[] | {name, online, last_seen}'

Remove offline agent

curl -X DELETE -H "Authorization: Bearer $TOKEN" \
  "http://hub:8080/api/agents/remove?name=<agent-name>"

FAQ

Port 8080 already taken?

Edit .env:

LISTEN=:9090

Also update the compose ports mapping to 9090:9090, and each remote agent's AGENT_URL=http://host:9090.

On macOS, suggested ports miss my local Node/Nginx?

Known Docker Desktop limitation. Dockpit runs inside the Docker Desktop Linux VM; net.Listen checks ports inside the VM, not on the macOS host. Ports published by Docker containers are detected correctly via the API. Not an issue on Linux hosts.

Agent shows online but requests 502?

The hub can receive heartbeats ≠ the hub can reach the agent. Common causes:

  1. Firewall on the agent blocks inbound 8080
  2. AGENT_URL points to the agent's own localhost, unreachable from the hub
  3. Networking misconfigured inside the agent container

Debug: run curl -v http://<AGENT_URL>/api/health from the hub; should return {"status":"ok"}.

Agent disconnected but still shows online?

Expected. The hub uses heartbeats — offline takes 90s. To remove immediately, call DELETE /api/agents/remove?name=X.

Hub restart — all agents go offline?

The registry is in-memory. On the agents' next heartbeat (within 30s) they get a 404 and re-register automatically. Recovery: at most 30–60s.

Agent mode refuses to start with "TOKEN is required"?

Agent mode mandates a token. Correct steps:

cp .env.example .env
vi .env                                             # fill in TOKEN etc.
docker compose -f docker-compose.agent.yml up -d

Security

Token protection

  • .env is in .gitignorenever commit it
  • chmod 600 .env to restrict permissions
  • In production, integrate with Vault / secret managers

Network

Current version assumes a trusted network:

  • Browser → hub: no auth
  • Hub ↔ agent: bearer token
  • Transport not encrypted

For production:

  • Terminate TLS with nginx / Caddy in front of the hub
  • If hub ↔ agent crosses the public internet, use WireGuard / Tailscale
  • Or add mTLS (not implemented today)

Least privilege

Each agent mounts /var/run/docker.sock read-only (:ro). Note: read-only Docker socket still allows dangerous operations (read sensitive images, inspect all container env vars). Read-only is limited protection, not a full sandbox.

Deploy agents only on trusted hosts. Do not run in shared multi-tenant environments.

Restart authorization

The UI has no per-user permission model — anyone who can reach the hub UI can restart any container on any agent. If you need access control:

  • Front the hub with nginx basic auth / OAuth2 proxy
  • Or extend Dockpit with per-agent authorization (not implemented)

Development

Local build

Go standard library only, no external dependencies:

go build -o dockpit .
./dockpit               # default MODE=both on :8080

Integration test (local federation)

go build -o /tmp/pc .
MODE=both TOKEN=test AGENT_NAME=central AGENT_URL=http://localhost:8080 \
  /tmp/pc > /tmp/hub.log 2>&1 &
MODE=agent TOKEN=test HUB_URL=http://localhost:8080 \
  AGENT_NAME=remote1 AGENT_URL=http://localhost:8099 LISTEN=:8099 \
  /tmp/pc > /tmp/agent.log 2>&1 &

curl -s http://localhost:8080/api/agents | jq
curl -s "http://localhost:8080/api/containers?agent=remote1" | jq

Build the image

docker build -t dockpit:latest .
# or
docker compose build

Multi-stage build:

  • builder: golang:1.22-alpine + GOPROXY=https://goproxy.cn,direct
  • runtime: alpine:3.19 + tzdata (dropped after Asia/Shanghai is set)
  • Final size ~10MB

Project Structure

Dockpit/
├── main.go                     # entry point, mode dispatch
├── config.go                   # env-var parsing
├── docker.go                   # Docker Engine API calls
├── agent.go                    # agent handlers + bearer middleware + register/heartbeat
├── hub.go                      # hub registry, heartbeat handlers, dispatcher
├── registry.go                 # thread-safe agent registry (RWMutex + TTL)
├── proxy.go                    # SSE-friendly reverse proxy
├── html.go                     # embedded Web UI (SVG sprite + CSS + JS)
├── go.mod                      # Go module (no external deps)
│
├── Dockerfile                  # multi-stage build
├── docker-compose.yml          # hub / both mode (default, single host)
├── docker-compose.agent.yml    # agent mode (remote nodes)
├── .env.example                # env var template
├── LICENSE                     # Apache 2.0
├── NOTICE                      # copyright notice
└── README.md                   # this file

Zero external dependencies

$ cat go.mod
module dockpit

go 1.22

Everything implemented on the standard library:

  • HTTP / SSE → net/http + net/http/httputil
  • Docker API → net/http + net.Dialer (Unix socket)
  • JSON → encoding/json
  • Concurrency → sync + context
  • Regex (ANSI stripping) → regexp

References


License

This project is licensed under the Apache License 2.0.

Copyright 2026 Beijing Aiyu Intelligent Technology Co., Ltd.
                (北京艾语智能科技有限公司)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Contact

About

Lightweight Docker container management Web UI — Go stdlib only, ~10MB image, Hub + Agent multi-node support.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors