The Plotwork engine as one container: MCP and the drawing API, no outbound request.
296
The Plotwork diagram engine as one container, for teams whose diagram text must stay inside their own network. Your AI agents and services send it a few lines of text and get back clean, laid-out SVG diagrams: architecture and flow diagrams, sequence diagrams, database schemas, Gantt charts, mind maps, C4, and 20 more kinds.
/mcp, for Claude Code, Cursor, VS Code and any MCP client/v1, the same paths and bodies as the hosted Plotwork API/healthzNo accounts, no hosting, no telemetry, and no outbound request. Diagram text is worked on in memory and never stored or logged.
| Image | plotwork/selfhost, also ghcr.io/plotwork-official/plotwork-selfhost (same digest) |
| Tags | A version such as 1.0.0 for production. latest follows stable releases, beta the previews |
| Platforms | linux/amd64, linux/arm64 |
| Base | Distroless Node 24, pinned by digest. No shell, no package manager |
| Size | About 55 MB |
| User | 65532, not root. Runs on a read-only root filesystem and writes nothing |
| Port | 3106 |
| Signed | Sigstore signature, build provenance and SBOM on every image |
| Source and releases | github.com/plotwork-official/plotwork-selfhost |
| Docs | docs.plotwork.dev/self-hosting |
Make a token (at least 32 characters) and keep it in your secret store:
export PLOTWORK_TOKEN="$(openssl rand -hex 32)"
Start the container:
docker run -d --name plotwork \
--read-only --cap-drop ALL --security-opt no-new-privileges \
-p 127.0.0.1:3106:3106 \
-e PLOTWORK_TOKEN \
plotwork/selfhost:latest
Check it:
curl -s http://127.0.0.1:3106/healthz
An internal network has no route out, so the container cannot reach the internet even if something asked it to. Services that call Plotwork join the plotwork network and reach it at http://plotwork:3106. Put PLOTWORK_TOKEN in a .env file beside this one.
services:
plotwork:
image: plotwork/selfhost:latest
restart: unless-stopped
environment:
PLOTWORK_TOKEN: ${PLOTWORK_TOKEN:?Set PLOTWORK_TOKEN in .env}
PLOTWORK_WORKERS: '2'
networks:
- plotwork
read_only: true
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
mem_limit: 1g
pids_limit: 64
networks:
plotwork:
internal: true
Start from examples/kubernetes.yaml: two non-root, read-only replicas with health probes, a Service, and a NetworkPolicy with no egress. Create the token as a Secret:
kubectl apply -f kubernetes.yaml
kubectl -n plotwork create secret generic plotwork --from-literal=token="$(openssl rand -hex 32)"
Point any MCP client at http://<host>:3106/mcp with the header Authorization: Bearer <token>.
Claude Code
claude mcp add --transport http plotwork http://plotwork.internal:3106/mcp \
--header "Authorization: Bearer $PLOTWORK_TOKEN"
Cursor (~/.cursor/mcp.json)
{
"mcpServers": {
"plotwork": {
"url": "http://plotwork.internal:3106/mcp",
"headers": { "Authorization": "Bearer ${env:PLOTWORK_TOKEN}" }
}
}
}
A service over REST
curl -X POST http://plotwork.internal:3106/v1/diagrams/render \
-H "Authorization: Bearer $PLOTWORK_TOKEN" \
-H "content-type: application/json" \
-d '{"source": "flow \"Checkout\"\nnode web service \"Web\"\nnode db database \"Orders\"\nweb -> db"}'
VS Code and more are in the self-hosting guide.
Every setting is an environment variable, read once at start. A value the server cannot use stops it with a message that names the variable.
| Variable | Default | Meaning |
|---|---|---|
PLOTWORK_TOKEN | none | Bearer token for /mcp and /v1, at least 32 characters |
PLOTWORK_HOST | 0.0.0.0 in the image | Address to listen on |
PLOTWORK_PORT | 3106 | Port to listen on |
PLOTWORK_ALLOW_OPEN | unset | 1 serves without a token beyond loopback. Only where the network itself is the control |
PLOTWORK_ALLOWED_ORIGINS | none | Browser origins allowed to call, comma-separated |
PLOTWORK_WORKERS | 2 | Worker threads that run layouts. One layout uses one thread for up to a second |
PLOTWORK_QUEUE | 32 | Jobs that may wait for a worker before more are refused |
PLOTWORK_TIMEOUT_MS | 30000 | Deadline of one job, waiting included |
PLOTWORK_WORKER_MEMORY_MB | 512 | Heap limit of each worker |
PLOTWORK_MAX_BODY_BYTES | 4194304 | Largest request body |
PLOTWORK_MCP_RESULT_LIMIT | 44000 | Characters of an MCP drawing answer before its pictures are left out |
PLOTWORK_TOOLS | full | core lists only the eight workflow tools, for open models with a small context |
PLOTWORK_LICENSE | none | Your commercial licence key. Shown in /healthz as a fingerprint, never enforced |
cosign verify plotwork/selfhost:<version> \
--certificate-identity-regexp '^https://github.com/plotwork-official/plotwork-selfhost/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
gh attestation verify oci://plotwork/selfhost:<version> --repo plotwork-official/plotwork-selfhost
Then push it into your own registry, so your hosts pull it from inside your network.
| Topic | The server | You |
|---|---|---|
| Transport | Plain HTTP | Put TLS in front: a reverse proxy, a service mesh or an ingress |
| Authentication | One static bearer token, compared in constant time | Keep it in a secret store, one per environment, and rotate it by restarting |
| Outbound network | Makes no request; fetch and socket clients are switched off in the process | Block egress anyway, with an internal network or a network policy |
| Browsers | Refuses an Origin not in PLOTWORK_ALLOWED_ORIGINS | List only your own tools' origins |
| Limits | Body cap, bounded queue, a deadline on every job, a heap cap per worker | Set container CPU and memory limits; about 250 MB at rest with two workers |
| Logs | One JSON line per request and per tool call, to standard output, never with diagram text | Ship them where you keep logs |
Report a security problem to [email protected] with "Security" in the subject.
Accounts, the portal, hosted diagram links, organisations, brand themes and custom icon packs are features of the hosted service at plotwork.dev. read_diagram and compare_diagrams take SVG text or source here, not a hosted link, and drawings carry no viewUrl.
Released under the Plotwork Self-Hosted Licence. Free for:
Any other organisation needs a commercial licence, the self-hosting add-on to Plotwork Enterprise. The licence and every bundled component's licence are in the image under /licenses.
Plotwork is made by Buun Group.
Content type
Image
Digest
sha256:36d31246d…
Size
56 MB
Last updated
about 7 hours ago
docker pull plotwork/selfhost