Container Manager
The Container Manager is the Server Mode control panel for every Docker container on your node. It replaces the trip to a terminal for docker run, docker exec, and docker-compose up with a focused UI that still routes every privileged action through the audited sysctl whitelist so you always have a trail of who did what.
Where Installed Apps manages catalogue-driven installs, Container Manager is for everything else: ad-hoc containers, custom compose stacks, private-registry images, and the quick exec into a running shell.
How It Works
- Read-only views (list, inspect, image library) talk to the Docker daemon via the official SDK over
/var/run/docker.sock. - Mutating actions (start / stop / restart / rm / run) go through
internal/sysctlso every click lands in thesysctl_audittable. The daemon socket itself is never exposed to the browser. - Container exec opens a WebSocket to
/ws/containers/{id}/execthat pipes a realdocker exec -itPTY through xterm.js. - Image registries — private-registry credentials live in the
image_registriestable. Passwords are encrypted at rest with AES-256-GCM via the sharedinternal/crypto/secretshelper; the host key lives in a 0600 file outside the database so a DB dump alone isn’t enough to recover them.
Tabs
The page is split into three tabs that share the same header button Run container so creating something new is always one click away:
Containers
A live table of every container on the host with:
- Status pill (running / paused / exited / restarting).
- Mini CPU + memory sparkline per running row, fed by the existing
/api/docker/containers/{id}/stats/streamSSE endpoint — the same stream that powers the Dashboard Docker widget, so no extra daemon load. - Port mappings (
host:container/proto) and Quazzar-managed badge so you can tell at a glance which rows the App Store owns. - Action menu: Restart, Inspect, Exec shell, Remove (with confirmation).
Compose
Paste a docker-compose.yml into the textarea or drop a file onto the dropzone. Cloud OS parses the YAML server-side, shows a preview card per service (image, ports, volumes, env-var keys, restart policy) and any warnings about unsupported fields (e.g. build: without image: is skipped with a warning rather than failing the whole import). Click Deploy stack to pull + run each service in alphabetical order.
The importer understands a pragmatic subset of compose v3:
image,environment(both map + list form),ports,volumes,restart,command,labels,networks.- Unsupported today:
build:,configs:,secrets:,deploy:. They’re dropped with a warning rather than failing silently.
Images
A one-screen view of image inventory, prune, and registry credentials:
- Pull any image by reference (
nginx:1.25,ghcr.io/org/app:latest, …). If a matching private registry is configured, the pull auto-logs-in first. - Prune dangling one-click cleanup reports reclaimed bytes.
- Delete individual images; rows already used by a container are disabled.
- Private registries — add credentials for
ghcr.io,registry.gitlab.com, or a self-hosted registry. The password never echoes back on list.
Exec Shell
The exec terminal opens a modal with xterm.js hooked to a WebSocket. The wire protocol is identical to the Web Terminal:
- Binary frames carry raw PTY bytes in both directions.
- Text frames are JSON control messages — currently only
{"type":"resize","cols":<n>,"rows":<n>}.
The default command is /bin/sh — containers that include bash can request it via a ?cmd=/bin/bash query param (the terminal falls back automatically if the requested binary isn’t present).
Security Model
Every mutating click funnels through sysctl.Service.Dispatch with one of the whitelisted verbs (docker.start, docker.stop, docker.restart, docker.rm). The audit table records user, action, target container ID, exit code, duration, and truncated stdout/stderr per call — so an operator reviewing the audit log can see exactly which container was stopped and by whom. Registry credentials never leave the server; when a pull needs them, the backend issues a docker-login on your behalf and the password stays encrypted in the DB.
API Reference
| Endpoint | Description |
|---|---|
GET /api/containers | List every container on the host. |
GET /api/containers/{id} | Detailed inspect (env, mounts, networks, cmd). |
POST /api/containers/{id}/{start|stop|restart} | Lifecycle actions. |
DELETE /api/containers/{id} | Force-remove a container. |
POST /api/containers/run | Create + start from a {image, name?, env, ports[], volumes[], restart_policy} body. |
POST /api/containers/compose/preview | Parse a compose YAML, return services + warnings. |
POST /api/containers/compose/deploy | Deploy a parsed compose stack. |
GET /api/containers/images | List images with size, age, used-by count. |
DELETE /api/containers/images/{id} | Delete an image. |
POST /api/containers/images/prune | Prune dangling images. |
POST /api/containers/images/pull | Pull an image (uses stored registry creds if applicable). |
GET /api/containers/registries | List configured registries (no passwords). |
POST /api/containers/registries | Add / update a registry credential. |
DELETE /api/containers/registries/{id} | Delete a registry credential. |
GET /ws/containers/{id}/exec | WebSocket exec endpoint. |
All routes are session-authenticated — a browser cookie is required.