OpenIslands

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 by oi.app(id) inside an execute script.

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:latest

Or with Compose — drop this docker-compose.yml next to a ./project directory and bring it up:

docker-compose.yml
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:/project
docker compose up -d

The 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):

VariableDefaultWhat it does
OPENISLANDS_BIND127.0.0.1Host 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_PORT4321Host port that maps to the container's 4321 (compose only).
OPENISLANDS_MCP11/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_PORT4321Port the server listens on inside the container.
OPENISLANDS_HOST0.0.0.0Interface 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 set OPENISLANDS_BIND=0.0.0.0 (or publish the port directly with docker run -p).
  • The token is the auth boundary on the MCP write surface. Binding off loopback with MCP on requires OPENISLANDS_MCP_TOKEN or 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 with openssl rand -hex 32.
  • The IP allowlist is the network ACL on the runtime port. Once the port is reachable across your LAN, OPENISLANDS_ALLOWED_IPS decides which client IPs may connect at all — it gates the dashboard, the API, server functions, and /mcp alike, 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

TagPlatformsUse
:latest, :X.Y.Z, :X.Ylinux/amd64, linux/arm64The artifact to deploy. ARM builds cover ARM NAS boxes and Apple silicon.
:main, :sha-<commit>linux/amd64Rolling build of the tip, for tracking development.
  • MCP Server: the tool surface and safety posture, identical over HTTP.
  • CLI: serve --mcp runs the same thing without Docker.

On this page