Blacknode includes a Docker Compose path for local, cloud VM, and on-prem demos. It starts three services. Local development can build from source, while published deployments can use prebuilt GHCR images.
| Service | Port | Purpose |
|---|---|---|
editor |
3000 |
React visual editor. |
editor-server |
7777 |
FastAPI backend, workflow store, run store, cook API. |
blacknode-mcp |
9901 |
Streamable HTTP MCP server at /mcp. |
The dev stack (docker-compose.yml) publishes these ports for local access. The
production stack (docker-compose.published.yml) does not: editor-server
is internal-only, editor/blacknode-mcp bind to loopback, and public access
goes through the Caddy proxy profile. See
Production deployment.
On Windows, use the helper so Docker Desktop/engine problems get a clear actionable message:
.\docker-up.ps1On Linux, macOS, or after confirming docker info works:
docker compose up --buildhttp://127.0.0.1:3000
Load a template, click Cook on an Output node, and inspect the Runs tab.
http://127.0.0.1:9901/mcp
MCP clients connect to that endpoint with streamable-http. A browser GET can
show a protocol response instead of a page; use an MCP client to list tools.
To use hosted NVIDIA NIM, create a local .env from the example and set
NVIDIA_API_KEY:
cp .env.example .envCopy-Item .env.example .envThen edit .env and start the stack:
.\docker-up.ps1Local state mounted from the checkout:
workflows/editor-server/runs/
This Compose stack is the local and self-hosted evaluation path for the visual editor, backend, persisted run history, saved workflows, and streamable HTTP MCP endpoint.
After the images are published, use:
docker compose -f docker-compose.published.yml upSee Docker Publishing for GHCR publishing and image tags.
Security boundary — read first. The
editor-serverexecutes user-authored Python during a cook (PythonFnand the Script editor) and has no built-in authentication. Anyone who can reach it can run arbitrary code on the host and read any key typed into the UI. Treat it as a trusted-operator tool: never publish7777,3000, or9901to a public interface.docker-compose.published.ymlbinds the editor and MCP to loopback only and exposes the stack to real users solely through an authenticating TLS reverse proxy (Caddy, theproxyprofile).
cp .env.example .envSet, in .env:
-
Model keys (
NVIDIA_API_KEY,OPENAI_API_KEY,ANTHROPIC_API_KEY, …). Provide keys here via env, not through the editor UI key panel — env keys are never written toeditor-server/api_keys.jsonand are never returned by the/settings/api-keysendpoint. -
BLACKNODE_IMAGE_TAG— pin to a published 12-char commit SHA instead oflatestfor reproducible deploys. -
BLACKNODE_DOMAIN— the public hostname (a real domain gets an automatic Let's Encrypt certificate;localhostuses Caddy's internal CA for testing). -
BLACKNODE_BASICAUTH_USERandBLACKNODE_BASICAUTH_HASH. Generate the hash:docker run --rm caddy:2.8-alpine caddy hash-password --plaintext 'your-password'
docker compose -f docker-compose.published.yml --profile proxy up -dCaddy terminates TLS, enforces Basic Auth on every request, and reverse-proxies
to the internal editor (/) and MCP (/mcp) services. Only Caddy binds the
public 80/443 ports; the editor (3000) and MCP (9901) stay on loopback,
and editor-server (7777) is never published.
Open https://<your-domain>/; MCP clients connect to https://<your-domain>/mcp.
Without --profile proxy, the stack still starts but is reachable only at
http://127.0.0.1:3000 (and :9901) on the host — use that for a single
trusted operator on the box, never for remote users.
restart: unless-stoppedand healthchecks on every service.editor-serverruns as a non-root user (appuser, uid 10001).- Per-service CPU/memory limits (
deploy.resources.limits). - An isolated
blacknodebridge network.
State lives in named Docker volumes (survives down/up and reboots):
| Volume | Holds |
|---|---|
blacknode_workflows |
Saved workflow JSON. |
blacknode_runs |
Run history / replay records. |
caddy_data |
TLS certificates and keys. |
The live editing canvas (editor-server/blacknode_graph.json) is scratch state
in the container layer, not a volume — it resets when the container is
recreated (e.g. on an image update). Durable artifacts are the saved
Workflows and Runs above, so save work to a workflow before redeploying.
Back one up:
docker run --rm -v blacknode_runs:/data -v "$PWD:/backup" alpine \
tar czf /backup/runs-backup.tar.gz -C /data .The proxy adds TLS + a shared Basic Auth credential — enough for a small trusted group. It does not add per-user accounts, RBAC, or workspace isolation, and the cook surface still runs arbitrary code under that one credential. For untrusted or multi-tenant users, put Blacknode behind SSO/an identity-aware proxy and run one isolated instance per trust boundary.