PoB-Enhanced

Deployment guide

PoB-Enhanced is one container. It needs no database, keeps its few files (your current build, saved builds and item art) in /data, and serves the web UI, the REST API and the MCP endpoint on a single port (8080). Pick the setup that matches where you want it to run.

Choose a setup

There is no login. Anyone who can reach the port can view and change the loaded build. Keep it on your own PC, a trusted home network, or a tailnet. Never forward the port to the internet.

Docker Desktop (Windows and macOS)

  1. Install Docker Desktop and start it. On Windows, accept the WSL 2 option when the installer asks.
  2. Open a terminal (PowerShell on Windows, Terminal on macOS) and run:
    docker run -d --name pob-enhanced --restart unless-stopped -p 8080:8080 -v pob2-data:/data ghcr.io/skitty4fingers/pob-enhanced:latest
    The first start downloads the image (a few hundred MB). -v pob2-data:/data keeps your builds when the container is replaced by an update.
  3. Open http://localhost:8080. Continue with First run.

Port 8080 taken? Use another host port, for example -p 8090:8080, and open http://localhost:8090.

Docker Compose

Compose keeps the settings in a file and makes updates one command.

  1. Download the Compose file:
    curl -O https://raw.githubusercontent.com/Skitty4fingers/PoB-Enhanced/main/compose.image.yml
  2. Optional: create a .env file next to it (see Settings), for example:
    POB2_PORT=8080
    POB2_BIND=127.0.0.1
  3. Start it:
    docker compose -f compose.image.yml up -d

The Compose file listens on 127.0.0.1 by default, so only this machine can reach it, and keeps your builds in the pob2-data volume. See the next section to open it up.

Open it to your network

To use it from another PC, a tablet or your phone on the same network:

  1. In .env, set POB2_BIND=0.0.0.0 (Compose only; docker run -p 8080:8080 already listens on every address).
  2. Set POB2_ALLOWED_HOSTS to the names and IPs other devices will type, for example POB2_ALLOWED_HOSTS=gaming-pc,192.168.1.20. The web UI works without it; the MCP endpoint (/mcp) rejects unknown host names to block DNS-rebinding attacks.
  3. Restart: docker compose -f compose.image.yml up -d, then browse to http://192.168.1.20:8080 from the other device.

TrueNAS SCALE

  1. Open Apps → Discover Apps and click Custom App.
  2. Application name: pob-enhanced.
  3. Image: repository ghcr.io/skitty4fingers/pob-enhanced, tag latest, pull policy Always pull (so a restart picks up new versions).
  4. Environment variables: add POB2_ALLOWED_HOSTS with your NAS name and IP, for example truenas,192.168.1.147.
  5. Network → Ports: container port 8080, host port 8080 (or any free port), protocol TCP.
  6. Storage: add a host path (for example a dataset apps/pob2) mounted at /data, so builds survive updates. Give it to user ID 1000, or use an ixVolume. Click Install.
  7. When the app shows Running, open http://<nas>:8080.

To update, stop and start the app (with Always pull it fetches the newest image).

Unraid

  1. Go to Docker → Add Container.
  2. Name: pob-enhanced, Repository: ghcr.io/skitty4fingers/pob-enhanced:latest, Network type: Bridge.
  3. Add another Path, Port, Variable… → Port: container 8080, host 8080.
  4. Add another Path, Port, Variable… → Variable: key POB2_ALLOWED_HOSTS, value tower,192.168.1.30 (your server's name and IP).
  5. Add another Path, Port, Variable… → Path: container /data, host /mnt/user/appdata/pob-enhanced.
  6. Click Apply, then open http://<server>:8080. Update from the Docker tab with Check for updates.

Portainer or Synology Container Manager

Create a stack (Portainer: Stacks → Add stack; Synology: Container Manager → Project → Create) with this Compose file:

services:
  pob2:
    image: ghcr.io/skitty4fingers/pob-enhanced:latest
    ports:
      - "8080:8080"
    environment:
      POB2_ALLOWED_HOSTS: "nas,192.168.1.20"   # the names/IPs you browse to
    volumes:
      - pob2-data:/data                         # current build and saved builds
    restart: unless-stopped
volumes:
  pob2-data:

Deploy it and open http://<nas>:8080. To update, pull the image again and redeploy the stack.

Tailscale (private HTTPS, no open ports)

compose.tailscale.yml puts PoB-Enhanced behind a Tailscale sidecar with a real certificate. Only devices on your tailnet can reach it, at https://pob2.<tailnet>.ts.net.

  1. In the Tailscale admin console, enable MagicDNS and HTTPS Certificates, and note your tailnet name (like tail1234.ts.net).
  2. Create an auth key.
  3. Get the code and settings:
    git clone https://github.com/Skitty4fingers/PoB-Enhanced.git
    cd PoB-Enhanced
    cp .env.example .env      # set TS_AUTHKEY and TS_TAILNET
  4. Start it:
    docker compose -f compose.tailscale.yml up -d --build
  5. Open https://pob2.<tailnet>.ts.net from any device on your tailnet.

Without an auth key, docker compose -f compose.tailscale.yml logs tailscale prints a login link. Funnel (public exposure) is disabled.

Build from source

git clone https://github.com/Skitty4fingers/PoB-Enhanced.git
cd PoB-Enhanced
docker compose -f compose.enhanced.yml up -d --build

The first build takes a few minutes: it compiles LuaJIT and the web UI. To update, run git pull and the same command again.

Without Docker (Linux, macOS, WSL)

You need Python 3.10+, uv, Node 22+, git and a C compiler.

git clone https://github.com/Skitty4fingers/PoB-Enhanced.git
cd PoB-Enhanced
scripts/setup-engine.sh                  # builds LuaJIT + lua-utf8, installs Python deps, builds the web UI
export PATH="$HOME/.local/bin:$PATH"
uv run --directory python pob2-web       # http://127.0.0.1:8080

PoB needs LuaJIT from commit 2460b3ff (2026-07-20) or newer; distro packages are too old. The setup script builds the right one into ~/.local.

First run

  1. Click Import and paste a PoB share code, a pobb.in / pastebin link, or your poe.ninja character page: https://poe.ninja/poe2/profile/<account>/<league>/character/<name>.
  2. Open the Config tab and set enemy conditions, buffs and charges to how you play.
  3. Open the Help tab (or the ? button) for a tour of every tool.
  4. Later, press Update to re-import your character. Config settings are kept.

Your build is saved after every change and comes back after a restart. Use Builds to keep named copies, and Undo (Ctrl+Z) to step back through any change.

Connect an AI agent (MCP)

The MCP endpoint is http://localhost:8080/mcp (Streamable HTTP). The app's AI / MCP tab shows these settings filled in with your server's address. Agents share the live build with the web UI, the UI refreshes as they work, and anything they change can be undone.

On your computer

These run where you are, so they reach the server directly. From another machine, use the server's address (for example http://192.168.1.20:8080/mcp) and add that name or IP to POB2_ALLOWED_HOSTS.

Claude Code
claude mcp add --transport http pob2 http://localhost:8080/mcp
Claude Desktop

The config file starts local programs, so bridge with mcp-remote (needs Node.js). Settings → Developer → Edit Config, then restart Claude. For a non-localhost http:// address add "--allow-http" to the args.

{
  "mcpServers": {
    "pob2": { "command": "npx", "args": ["-y", "mcp-remote", "http://localhost:8080/mcp"] }
  }
}
ChatGPT users: Codex CLI

OpenAI's agent for the terminal and IDE, signed in with your ChatGPT account.

codex mcp add pob2 --url http://localhost:8080/mcp

or in ~/.codex/config.toml:

[mcp_servers.pob2]
url = "http://localhost:8080/mcp"
Gemini users: Antigravity or Gemini CLI

Personal Google AI accounts moved from Gemini CLI to Antigravity in June 2026. Add the server in ~/.gemini/config/mcp_config.json (or type /mcp in the CLI); remote servers use serverUrl:

{ "mcpServers": { "pob2": { "serverUrl": "http://localhost:8080/mcp" } } }

Gemini CLI (API key or Code Assist for teams):

gemini mcp add --transport http --scope user pob2 http://localhost:8080/mcp
Cursor, VS Code, Cline, Zed and other MCP clients
{ "mcpServers": { "pob2": { "url": "http://localhost:8080/mcp" } } }

A stdio-only client can run its own copy instead (it does not share the web UI's build):

{ "mcpServers": { "pob2": { "command": "docker", "args": ["run", "--rm", "-i", "ghcr.io/skitty4fingers/pob-enhanced:latest", "pob2-mcp"] } } }

Self-hosted models

Run a local model and let it drive PoB-Enhanced. Pick a model with tool calling (Qwen 3, Llama 3.1 or newer, Mistral Small...). Clients running in Docker reach the server at host.docker.internal (on Linux start them with --add-host=host.docker.internal:host-gateway); add host.docker.internal to POB2_ALLOWED_HOSTS.

LM Studio (0.3.17 or newer)

Right sidebar → Program → Install → Edit mcp.json:

{ "mcpServers": { "pob2": { "url": "http://localhost:8080/mcp" } } }
Open WebUI with Ollama (0.6.31 or newer)
  1. As an admin: Admin Settings → External Tools (Integrations in some versions) → +.
  2. Type MCP (Streamable HTTP) (not OpenAPI), authentication None, URL http://host.docker.internal:8080/mcp.
  3. In a chat, enable the pob2 tools from the tools menu.
LibreChat
mcpServers:
  pob2:
    type: streamable-http
    url: http://host.docker.internal:8080/mcp

Cloud chat apps: ChatGPT, Grok, Gemini, Muse, Claude.ai

These connect from the provider's servers, so they need a public HTTPS address (they reject localhost and private addresses). A tunnel is the quickest way:

cloudflared tunnel --url http://localhost:8080      # Cloudflare quick tunnel, no account needed
ngrok http 8080                                    # or ngrok

Add the hostname it prints (for example word-word.trycloudflare.com) to POB2_ALLOWED_HOSTS and restart, then use https://<that-host>/mcp:

AppSetup
ChatGPTSettings → Apps & Connectors → Advanced settings → Developer mode, then Settings → Connectors → Create: URL, authentication No authentication. Use it from a chat's + menu. Which plans allow custom connectors varies; check OpenAI's help pages.
Grokgrok.com/connectors → New Connector → Custom (paid plans). Through the xAI API, pass {"type": "mcp", "server_label": "pob2", "server_url": "https://…/mcp"} as a tool.
Gemini appConnected Apps → add custom, where your plan offers it (it rolled out with Gemini Spark on Google AI Ultra). Otherwise use Antigravity above.
Meta MuseNo MCP menu yet. Ask Muse to connect to the MCP server at your URL itself (it can save that as a skill), or add a Custom Connector for the REST API from https://…/openapi.json.
Claude.aiSettings → Connectors → Add custom connector.

There is no login. While the tunnel runs, anyone who learns its address can view and change the loaded build (Undo brings it back; nothing else on your machine is exposed). Keep the address private and stop the tunnel when you are done.

Updating

SetupUpdate
docker rundocker pull ghcr.io/skitty4fingers/pob-enhanced:latest, then docker rm -f pob-enhanced and run the original command again (the pob2-data volume keeps your builds)
Composedocker compose -f compose.image.yml pull && docker compose -f compose.image.yml up -d
TrueNASStop and start the app (pull policy Always)
UnraidDocker tab → Check for updates → Apply update
Source / no Dockergit pull, then rebuild or rerun scripts/setup-engine.sh

Settings

VariableDefaultWhat it does
POB2_PORT8080Host port (Compose files)
POB2_BIND127.0.0.1Address to listen on (Compose). 0.0.0.0 makes it reachable from your network
POB2_ALLOWED_HOSTSnoneExtra host names accepted by /mcp, comma-separated. localhost always works
POB2_IMAGEghcr.io/skitty4fingers/pob-enhanced:latestImage for compose.image.yml
POB2_DATA/dataFolder for the current build, saved builds and item art (mount a volume here)
TS_AUTHKEY, TS_TAILNETnoneTailscale setup (compose.tailscale.yml)

Troubleshooting

“port is already allocated” or the page won't load

Another program uses port 8080. Pick another host port (-p 8090:8080 or POB2_PORT=8090) and open that port instead.

It works on the server but not from my other PC or phone

With Compose, set POB2_BIND=0.0.0.0 and restart. Check the server's firewall allows the port.

The AI agent can't connect to /mcp (error about the Host header)

Add the exact name or IP the agent uses to POB2_ALLOWED_HOSTS, for example POB2_ALLOWED_HOSTS=nas,192.168.1.20, and restart the container.

My build disappeared after an update

The container had no volume at /data, so the new container started empty. Add one (see your setup above), then import again or press Update. From then on builds survive updates.

'=' expected near '+' when running without Docker

Your LuaJIT is too old. Run scripts/setup-engine.sh and make sure ~/.local/bin comes first on your PATH (or set POB2_LUAJIT).

The passive tree shows plain circles

The tree artwork is missing. It ships in tree-art/ and in the Docker image; if you moved the folder, set POB2_TREE_ART to its path.

Numbers look wrong

Check the Config tab first: enemy conditions, buffs and charges change every number. Mods PoB can't model are flagged “unmodelled” and count as zero.

Still stuck? Open an issue with your setup and the container log (docker logs pob-enhanced).