Manage Docker from Ervisio: containers, Compose stacks, images, volumes and networks, with live stats, logs, a shell in containers, attach, a file browser and commit to image, a create/edit wizard, templates, private registries, automatic updates, alerts and clean-up.
Until Ervisio 0.3.0 this plugin shipped inside Ervisio. It now lives here and is installed from the Ervisio marketplace; the build published there is signed by the Ervisio team.
In Ervisio, open Plugins › Browse, find Docker and choose Install. The dialog lists the permissions below; the plugin is signed with the Ervisio team key and Ervisio verifies the signature before installing. Updates appear in Plugins › Updates.
The manifest is plugin/manifest.json. In short:
- Docker Engine API on
/var/run/docker.sock(capabilities.http, nameddocker): only the methods and paths listed in its rules (containers, exec, file archives, commit, images, volumes, networks, system information, events, prune, registry login and image digest lookups). It is anadminentry: members of thedockergroup use it as themselves, other administrators unlock administrator rights. Access to the Docker API is equivalent to root on the machine. - Commands, all
adminwithadminUnlessGroup: docker:docker compose(up, pull, down, restart, stop, start, config, ls) for stacks,install -d -m 2775 -g docker /opt/stacksto create the stacks folder, andshell, a terminal (pty) runningdocker exec -it <container> <shell>, andattach, a terminal runningdocker attach --sig-proxy=false --detach-keys=ctrl-p,ctrl-q <container>. Every command declares"remote": "docker"and talks to{env}: this machine'sunix:///var/run/docker.sock, or the tunnel of an environment (see below). - Environments (Ervisio 0.5 or later): the
dockerAPI and everydockercommand declare"remote": "docker", so the plugin can work on the Docker hosts an administrator adds in Settings › Environments (Docker API with TLS, SSH, a Portainer Agent, or another Ervisio server). Against an environment calls run with the user's own rights through the user's tunnel;adminandadminUnlessGroupdo not apply. Stacks of a TLS, SSH or Portainer host keep their files on this server in/opt/stacks/.envs/<environment id>/<name>/(compose runs here with-H), so the stack commands take the folder prefix as a second argument. A paired Ervisio server keeps its stack files on itself (the pairing relays calls and commands, not file access). - Folders:
/opt/stacks(read and write,admin, for Compose stacks) and~/.config/ervisio/plugins/docker(created on first use; settings, registries, alerts, template sources). - Network: the plugin frame may fetch template lists from
raw.githubusercontent.comandgist.githubusercontent.com. - Visible to members of
docker,wheelandsudo(administrators always see it).
Requires Node.js 22 or newer.
npm ci
npm run build # typecheck, bundle to dist/docker/index.js, copy plugin/* next to it
npm run dev # rebuild on change
npm run pack # dist/docker-<version>.tar.gz and .sha256 (what a release publishes)Turn on developer mode in Ervisio (plugins.dev = true, or a daemon started with --dev) and load dist/docker from
Plugins › Developer; after a rebuild use "Reload" there. A dev folder runs unsigned while developer mode is on. If
the marketplace Docker plugin is installed, uninstall it first (the ids would clash).
The SDK types, the React shim and the Vite preset come from
@ervisio/plugin-sdk (a git dependency for now; .npmrc allows it for npm
12). Its documentation describes the SDK object.
On GitHub: Actions › Release › Run workflow, choose patch, minor or major, optionally type the release notes
(empty: the commit subjects since the last release), and run it. The workflow bumps the version, writes the
CHANGELOG.md section, tags, builds, validates and releases, then tells the Ervisio registry: an update that asks for
no new permissions is in the marketplace a few minutes later. The steps live in
Ervisio/plugin-sdk.
React is not bundled. The preset in vite.config.ts aliases react and the JSX runtime to the SDK's shim
(@ervisio/plugin-sdk/react), which forwards to sdk.react. The SDK exists only inside activate()
(src/index.ts), so:
- Never call a React API at module top level (
createContext,memo,forwardRef,lazy). Hooks and JSX inside components are fine. - Read the SDK with
getSdk()(src/sdk.ts) inside functions, never at import time. - Use the kit through
src/kit.ts(Button,Dialog,ConfirmDialog,toast, ...), notsdk.uidirectly.
plugin/manifest.json the manifest (unsigned; the registry adds "files" and signs)
plugin/templates.json the curated template catalog, shipped and signed with the plugin
src/index.ts activate: sets SDK and React, strings, styles, icons; registers the page and the widget
src/sdk.ts SDK v3 types and getSdk() (re-exported from @ervisio/plugin-sdk)
src/kit.ts typed wrappers for the app's UI kit
src/router.ts in-plugin router (Route type, navigate, back, useRoute, search box state)
src/api/ Docker Engine client and data layer
engine.ts docker.get/post/delete/stream, version negotiation, DockerError, classify()
streams.ts JsonLines, LogDemuxer, LogLines
stats.ts CPU/memory math and shared per-container stats streams
events.ts one shared /events stream
store.ts createResource(): polled + event-refreshed shared store
resources.ts containers, images, volumes, networks, info, diskUsage stores
hooks.ts useStats, useDockerEvents, useAsync
actions.ts start/stop/restart/remove, runBulk
model.ts stack grouping, health, ports, filters
format.ts bytes, percent, durations, relative time
src/settings.ts JSON files in ~/.config/ervisio/plugins/docker (settings, registries, alerts, templates-sources)
src/i18n/areas/*.ts strings, one file per area, merged automatically
src/styles/*.css CSS, every file is injected automatically
src/ui/ shared pieces: PageHeader, DataTable, DiskBar, StatusDot, Charts, ErrorState, ComingSoon
src/shell/App.tsx inner sidebar, search box, error gate
src/views/ one component per route, ViewHost in registry.tsx
- The route exists in
src/router.ts(Route). For a new one, add it there, add its nav section insectionOf, and render it insrc/views/registry.tsx. - Edit the view file in
src/views/. Its props are the route fields:RouteProps<'container'>is{ id, tab? }. Start with<PageHeader icon title subtitle actions back />. - Strings: create
src/i18n/areas/<area>.tsexporting{ en, it }with keys<area>.something. Uset('key'). - Styles: create
src/styles/<area>.css. Prefix classes withdk-, use theme variables only (--surface,--sunk,--ink*,--acc,--ok,--warn,--err,--h/--sinside ahue-*element, radii--r-*). No grey lines. - Data:
const { data, error, loading } = containers.use()for shared lists;docker.get/post/deletefor the rest;docker.streamfor logs, stats and pulls. Tell the user about failures withtoast.err(title, errorText(e)). - Go to another view with
navigate({ view: 'container', id });navigate(route, { root: true })starts a fresh history.back()goes back.
Every Engine path a view uses must be allowed by a rule in plugin/manifest.json (capabilities.http).
MIT, see LICENSE.