Real-time Docker architecture visualizer. Displays services, connections and metrics from all your Docker Compose projects in an interactive dashboard.
Existing tools show you numbers. ContainerFlow also:
- **Visualizes architecture** — interactive graph with connections (app→db, app→cache, proxy→app) auto-detected, not just a flat list
- **Detects sub-optimal config** — banners when a container runs without memory limits, CPU limits, or `restart: unless-stopped`. Teaches good practices while you use it
- **Measures real memory** — subtracts full page cache (active + inactive), not just inactive like `docker stats`. Your Postgres DB with hot buffers no longer reports a false 98%
- **Multi-tenant via path scoping** — `ALLOWED_PATHS` env var for shared servers: see everything, only touch what's yours
- **80 MB RAM, ~500ms startup** — Bun + Hono. A fraction of Portainer's footprint, starts faster than Grafana
- **[docs/docker-guide.md](./docs/docker-guide.md)** — Docker quick guide for using ContainerFlow: what each action does (Start, Stop, Restart, Recreate, Rebuild, Remove, Exec), restart policies, resource limits, volumes, healthchecks and FAQs. (Currently in Spanish; English translation in progress.)
- **[docs/roadmap.md](./docs/roadmap.md)** — Project roadmap: what's done, what's coming, what was discarded and why.
| `DATA_DIR` | `./data` | Persistence directory: stats history (`.dockerflow-stats.db`), Discord config (`.dockerflow-discord.json`), per-container overrides (`.dockerflow-container-settings.json`), node positions and env file overrides. Auto-created on startup. In Docker, mounted at `/app/data` via the `containerflow-data` volume. |
| `HOST_PROJECTS_DIR` | _(empty)_ | Additional path to mount so `rebuild`/`remove` can read compose files outside the defaults (`/home`, `/opt`, `/srv`, `/root`). Only needed for non-standard paths (e.g. `/data/apps`). |
| `ALLOWED_PATHS` | _(empty)_ | **Empty = everything actionable** (permissive mode). With values = `:`-separated list of prefixes; only containers whose compose file lives under one of these paths can execute actions, the rest appear with a lock icon. See [Security](#security) section. |
| `ALLOW_NON_COMPOSE` | `false` | **Only applies when `ALLOWED_PATHS` is active.** If `ALLOWED_PATHS` is empty, this has no effect. When applicable: `false` blocks actions on non-compose containers (started with `docker run` directly); `true` allows them. |
- **Real-time metrics** — CPU and memory per container, refreshed every 3 seconds
- **Docker events** — visual flash when a container starts, stops or restarts
- **Detail panel** — click a container to see info, stats, env vars and config in separate tabs
- **Container logs** — real-time logs with auto-scroll, stream filter (stdout/stderr) and copy option
- **Container actions** — start, stop, restart, rebuild, recreate and remove directly from the panel
- **Execute commands** — inline terminal (`docker exec`) from the DetailPanel with output, no SSH or external terminal needed
- **Error toasts** — when an action fails (broken rebuild, exec with non-zero exit code, etc.) a top-right toast shows the full error, copyable to clipboard
- **Path-based access control** — `ALLOWED_PATHS` env var lets you restrict actions to containers whose compose file lives under specific paths. Ideal for shared servers: see everything, only touch what's yours. Containers outside the paths appear with a lock icon
- **Docker config recommendations** — warning banners in the DetailPanel when a container has sub-optimal config: no memory limit, no CPU limit, no restart policy (`unless-stopped` recommended). Helps users adopt good Docker practices without having to remember them
- **Volumes and mounts** — DetailPanel lists each mount on the container: type (bind / volume / tmpfs), source on the host, destination in the container, rw/ro mode. Useful for debugging ("where's my data?", "is this read-only?", "is it persistent?")
- **Project filter** — dropdown to show/hide projects, persists across sessions
- **Authentication** — login screen with AUTH_TOKEN for secure remote access
- **Visual groups** — boxes per project/compose with title, compose file and container count
- **Context menu** — right-click on a node for quick actions
- **Monitoring page** — CPU/RAM history per service (1h, 6h, 24h, 7d) persisted in SQLite, sparkline charts, expand per container, filters by project/service, and a Docker events feed
- **Discord notifications** — configurable webhook for state changes, resource alerts, manual actions and errors
- **Per-container thresholds** — custom CPU/MEM overrides (with fallback to global thresholds) and notification toggle per service
ContainerFlow doesn't just monitor: it detects sub-optimal Docker configuration and flags it with an amber banner in the affected container's DetailPanel. The idea is to help you adopt good practices without having to remember them.
| **No `memory_limit`** | A container without a RAM cap can hog all host memory and take down everything else (including the daemon). The kernel does random OOM kills under pressure. | Banner: "No memory limit configured in Docker" |
| **No `cpu_quota`** | Similar to memory — a container can saturate all cores. Critical in multi-tenant, degrades host responsiveness in single-tenant. | Banner: "No CPU limit configured in Docker" |
| **`restart: no` or empty** | If the process dies, the container stays dead. In production you almost always want `unless-stopped` (restarts on crash, does **NOT** if you stopped it manually). | Banner: "Restart policy: none — the container will not restart automatically if stopped" |
- **Healthcheck recommendations**: detect containers without `HEALTHCHECK` and suggest a contextual one based on the image (postgres → `pg_isready`, redis → `redis-cli ping`, http app → `curl /health`, etc.)
- **Non-persistent mounts**: warning when a DB uses `tmpfs` or binds to an ephemeral directory
- **`:latest` tag**: warning when a container uses `image:latest` (not reproducible)
-`GET /api/stats/history?range=1h` — history of all services
-`GET /api/stats/history/:uid?range=1h` — history of a specific service
- **UI** — the monitoring page (`MonitoringPage.tsx`) shows a card per service with CPU and MEM sparklines, current value, average and threshold line. Each card can be expanded for a larger chart, and filtered by project and/or service (filters are cumulative).
| **Container State Changes** | `start`, `stop`, `die` (crash), `restart`, `health_status`. `stop`/`die` events are debounced 15s to detect restarts and send a single "Container Restarted" message instead of separate stop+start |
| **Resource Alerts** | A container's CPU or memory exceeds the threshold (global or per-container) |
| **UI Actions** | Manual action triggered from the panel: start/stop/restart/rebuild/remove |
| **Action Errors** | An action executed from the UI failed (includes the error message) |
- **Processing logic** (`src/client/hooks/processing.test.ts`) — state sync when actions are executed on containers (start/stop/restart), including crashed/dead states, timeouts and minDuration handling
- **Connection detection** (`src/server/docker.test.ts`) — discovery of service relationships via shared networks, service classification (infra, proxy, worker) and deduplication
- **Memory breakdown calc** (`src/server/watcher.test.ts`) — `computeMemoryBreakdown()` covering cgroup v1 and v2, fallback paths and edge cases
- **HTTPS required in production** — the auth token travels in HTTP headers. Without HTTPS, it's plaintext visible on the network. Use a TLS-terminating reverse proxy (nginx, Caddy, Cloudflare Tunnel) in front of ContainerFlow.
- **Rate limiting** — included by default: 5 failed attempts per minute per IP. After the limit, returns `429 Too Many Requests`. Applies to both REST API and WebSocket authentication.
- **Local access by default** — without `AUTH_TOKEN`, the server only listens on `127.0.0.1`. With `AUTH_TOKEN`, it listens on `0.0.0.0` for remote access.
- **Docker socket** (`/var/run/docker.sock`) — full daemon access. Equivalent to root on the host: can create privileged containers, mount any path, read/write the entire filesystem. If ContainerFlow is compromised, the host is compromised.
- **Read-only host mounts** — `docker-compose.yml` mounts `/home`, `/opt`, `/srv` and `/root` as `:ro` so the `rebuild` and `exec` actions can read compose files. Allows **read access** to files in those directories (including SSH keys, git credentials, etc. of any user on the system).
**Implications on a multi-user server:** if multiple users (`/home/jorge`, `/home/israel`, `/home/pedro`) have projects on the same host, ContainerFlow can read all their files. Docker socket access makes this relatively moot (anyone with the socket already has full host access), but worth being aware of.
- 🐛 **Bug?** Open an [issue](https://github.com/RGJorge/containerflow/issues/new?template=bug_report.md)
- 💡 **Idea?** Open a [feature request](https://github.com/RGJorge/containerflow/issues/new?template=feature_request.md)
- 💬 **Discussion / question?** Open a [discussion](https://github.com/RGJorge/containerflow/discussions)
- 🔒 **Security vulnerability?** Report privately — see [SECURITY.md](SECURITY.md)
- 📜 **Code of Conduct** — see [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)
- 🛠 **Want to contribute code?** — see [CONTRIBUTING.md](CONTRIBUTING.md). Currently we only accept issues; PRs will open as the project matures and patterns stabilize.