Self-hosting
Run OpenIslands as an always-on Docker container on a server or home NAS — the dashboard and the MCP server over HTTP in one process, from the ghcr image, with a token guarding the write surface off loopback.
openislands serve is already a long-running local app. To keep a dashboard always-on — on a
home server or NAS, reachable by a remote agent — run the published Docker image. One Node
process serves both surfaces on one port:
- the dashboard at
/, and - the MCP server over Streamable HTTP at a single
/mcp— one endpoint for the whole workspace, with the app picked byoi.app(id)inside anexecutescript.
Because both share the process, an MCP edit and the live dashboard stay in lockstep: when an agent applies a change, the file watcher fires and the open page live-updates over SSE — no second service, no second port. stdio stays available for local agent configs; HTTP is the addition for remote, always-on use.
Still local-first
This is your data on your hardware — one process, one port, one volume you mount. It is not a
hosted tier or a multi-tenant service; it's the same engine you run with serve, packaged to stay
up.
Quick start
The image is published to ghcr.io/lukaisailovic/openislands. Mount a project workspace at
/project — the directory holding your apps under apps/<id>/. serve scans them and serves them
all in one process, whether the workspace has one app or several.
Generate a token
The container binds 0.0.0.0 so its port is reachable, and ships with MCP on. MCP is a write
surface (it can edit your manifest and insert rows), so the server refuses to start off loopback
without a token. Make one:
export OPENISLANDS_MCP_TOKEN=$(openssl rand -hex 32)Running dashboard-only with no agent write path? Skip the token and set OPENISLANDS_MCP=0 instead
(see Security).
Run it
With Docker directly:
docker run -d --name openislands \
-p 127.0.0.1:4321:4321 \
-e OPENISLANDS_MCP_TOKEN \
-v "$PWD/my-dashboard:/project" \
ghcr.io/lukaisailovic/openislands:latestOr with Compose — drop this docker-compose.yml next to a ./project directory and bring it up:
services:
openislands:
image: ghcr.io/lukaisailovic/openislands:latest
container_name: openislands
restart: unless-stopped
ports:
- "${OPENISLANDS_BIND:-127.0.0.1}:${OPENISLANDS_HOST_PORT:-4321}:4321"
environment:
OPENISLANDS_MCP_TOKEN: ${OPENISLANDS_MCP_TOKEN:-}
volumes:
- ./project:/projectdocker compose up -dThe mounted /project must be writable by uid 1000 — the container runs as a non-root node
user and writes app state under .openislands/ there.
Open it
Visit 127.0.0.1:4321. Edit a file under the mounted project and the page
live-updates, exactly like local serve.
Configuration
Everything is environment-driven (CLI flags win if you also pass them):
| Variable | Default | What it does |
|---|---|---|
OPENISLANDS_BIND | 127.0.0.1 | Host interface the port maps to. Set 0.0.0.0 to reach it across your LAN/NAS (compose only — the host side of the port mapping). |
OPENISLANDS_HOST_PORT | 4321 | Host port that maps to the container's 4321 (compose only). |
OPENISLANDS_MCP | 1 | 1/true mounts the MCP HTTP endpoint. Set 0 for a dashboard-only container. |
OPENISLANDS_MCP_TOKEN | (unset) | Bearer token required on MCP requests. Required to bind off loopback with MCP on. |
OPENISLANDS_PORT | 4321 | Port the server listens on inside the container. |
OPENISLANDS_HOST | 0.0.0.0 | Interface the server binds inside the container. Leave as is. |
OPENISLANDS_ALLOWED_IPS | * (all) | IP allowlist for the whole port — comma-separated exact IPs and IPv4 CIDRs (e.g. 192.168.1.0/24). Loopback is always allowed; unset means any host that can reach the port may connect. |
Security
Two boundaries, both yours to set:
- The port mapping is the network boundary. It defaults to
127.0.0.1, so nothing is exposed beyond the host until you setOPENISLANDS_BIND=0.0.0.0(or publish the port directly withdocker run -p). - The token is the auth boundary on the MCP write surface. Binding off loopback with MCP on
requires
OPENISLANDS_MCP_TOKENor the server exits with a clear error — loopback alone is local trust (parity with stdio), but a write surface on the network is not. Generate one withopenssl rand -hex 32. - The IP allowlist is the network ACL on the runtime port. Once the port is reachable across
your LAN,
OPENISLANDS_ALLOWED_IPSdecides which client IPs may connect at all — it gates the dashboard, the API, server functions, and/mcpalike, before any handler runs. It defaults to allow-all and always permits loopback. It's an ACL, not authentication: for real per-user auth, front the container with a reverse proxy (Caddy/nginx).
Off-loopback needs a token
To expose OpenIslands on your LAN or NAS, set both OPENISLANDS_BIND=0.0.0.0 and
OPENISLANDS_MCP_TOKEN=$(openssl rand -hex 32). Lock it to just your devices with
OPENISLANDS_ALLOWED_IPS=192.168.1.0/24 (your LAN subnet). Want the dashboard reachable but no
agent write path at all? Set OPENISLANDS_MCP=0 and the container boots token-free — the /mcp
endpoint is simply not mounted.
Connecting an HTTP MCP client
Point an HTTP-aware MCP client at the single /mcp endpoint — http://<host>:4321/mcp — and pass
the token as a bearer header. One endpoint hosts the whole workspace; inside an execute script the
client picks the app with oi.app(id) (omit the id when there's only one), and oi.listApps()
enumerates them.
A client that takes a server URL — for example Nous Research's Hermes Agent, via its url: server
config — connects like this:
{
"mcpServers": {
"openislands": {
"url": "http://<host>:4321/mcp",
"headers": { "Authorization": "Bearer <your-token>" }
}
}
}A missing or wrong token returns 401. The same Code Mode surface (the execute tool + the oi
API) and read-many/write-one safety posture from the MCP page apply — the transport is the
only thing that changes. App ids never appear in the URL, so the single /mcp endpoint serves every
app in the workspace.
Image tags
| Tag | Platforms | Use |
|---|---|---|
:latest, :X.Y.Z, :X.Y | linux/amd64, linux/arm64 | The artifact to deploy. ARM builds cover ARM NAS boxes and Apple silicon. |
:main, :sha-<commit> | linux/amd64 | Rolling build of the tip, for tracking development. |
Related
- MCP Server: the tool surface and safety posture, identical over HTTP.
- CLI:
serve --mcpruns the same thing without Docker.