How to Configure Wigolo for Self-Hosting with Docker and Secure Token Authentication

To self-host Wigolo in Docker, you must bind the daemon to 0.0.0.0, provide a bearer token via WIGOLO_API_TOKEN or WIGOLO_API_TOKEN_FILE, and persist the /data directory to survive container restarts.

Wigolo is an open-source Node.js MCP (Model Context Protocol) server that exposes either a local stdio interface or an HTTP daemon. When configuring Wigolo for self-hosting with Docker and secure token authentication, the service follows a strict fail-closed security model: it refuses to start on non-loopback addresses unless authentication is explicitly configured. This guide covers the official deployment patterns using the repository's provided compose files and environment variable conventions.

Understanding the Security Model and Binding Requirements

Wigolo's daemon operates in two modes: stdio for local MCP clients and serve for HTTP-based remote access. When you deploy in Docker, you must expose the service beyond localhost using --host 0.0.0.0. According to the source code in docs/self-hosting.md (lines 34-44), the application enforces a critical safety check: if the bind address is not loopback (127.0.0.1 or localhost), the daemon validates that a bearer token is configured. If no token is present, the process exits immediately to prevent accidental unsecured exposure.

Docker Deployment Options

The repository provides two primary methods for containerized deployment: Docker Compose for production stability and docker run for quick testing.

The official compose file at packaging/compose.serve.yml defines the standard production configuration. It specifies the image ghcr.io/knockoutez/wigolo, exposes port 3333, and mounts a named volume to /data for persistence.

Key configuration elements from packaging/compose.serve.yml (lines 35-40) include:

  • Port mapping: Exposes container port 3333 to the host
  • Volume persistence: Uses a named Docker volume for /data to store on-device models, browser binaries, and cache between restarts
  • Health checks: Built-in HTTP health endpoint monitoring

To deploy:

docker compose -f packaging/compose.serve.yml up -d

Using Docker Run (Quick Start)

For immediate testing without Compose, generate a secure random token and launch the container directly:


# Generate a 32-byte hex token

TOKEN=$(openssl rand -hex 32)

docker run -d \
  -p 3333:3333 \
  -v wigolo-data:/data \
  -e WIGOLO_API_TOKEN=$TOKEN \
  ghcr.io/knockoutez/wigolo \
  serve --host 0.0.0.0 --port 3333

The --host 0.0.0.0 flag is mandatory for Docker networking, allowing the container to accept connections from outside its network namespace.

Configuring Authentication

Wigolo supports two methods for supplying the bearer token: inline environment variables for development and secret files for production security.

Inline Token Configuration (WIGOLO_API_TOKEN)

Set the WIGOLO_API_TOKEN environment variable when starting the container. Every request to REST endpoints (/v1/*) or the MCP endpoint (/mcp) must include the header:


Authorization: Bearer <token>

Example API call:

curl -H "Authorization: Bearer $TOKEN" \
     http://localhost:3333/v1/search \
     -d '{"query":"latest JavaScript async patterns"}' \
     -H "Content-Type: application/json"

Secret File Configuration (WIGOLO_API_TOKEN_FILE)

For production deployments, avoid exposing tokens in environment variables where they might leak via docker inspect or process listings. Instead, use the WIGOLO_API_TOKEN_FILE variable to reference a mounted secret file.

As implemented in packaging/compose.serve.yml (lines 42-45), bind-mount a secret file and reference it:


# Create secret file outside version control

echo "$TOKEN" > /run/secrets/wigolo_token

# Mount and configure

docker run -d \
  -p 3333:3333 \
  -v wigolo-data:/data \
  -v /run/secrets/wigolo_token:/run/secrets/wigolo_token:ro \
  -e WIGOLO_API_TOKEN_FILE=/run/secrets/wigolo_token \
  ghcr.io/knockoutez/wigolo \
  serve --host 0.0.0.0 --port 3333

This pattern keeps the token out of the container's environment table while maintaining the same authentication requirements.

Building the Docker Image

The Dockerfile in the repository root defines two build targets:

  1. default (slim): Contains the Node.js runtime but downloads the browser binary on first use into the persisted /data volume
  2. full: Pre-installs the browser binary for air-gapped or latency-sensitive environments

According to Dockerfile (lines 5-10 and 62-66), the default target is sufficient for most self-hosting scenarios. The first request triggers a lazy download of the browser binary into /data, which survives container restarts thanks to the volume mount.

To build the full image locally:

docker build --target full -t wigolo:full .

Connecting Clients to the Secured Endpoint

Once the container is running with token authentication, configure MCP-compatible agents to connect using the base URL and token:

wigolo config --set baseUrl=http://<host>:3333
wigolo config --set apiToken=$TOKEN

The agent will automatically include the Authorization: Bearer header when communicating with the /mcp endpoint. For direct REST API integration, always include the authorization header explicitly as shown in previous examples.

Summary

  • Fail-closed security: Wigolo refuses to start on 0.0.0.0 without a bearer token configured via WIGOLO_API_TOKEN or WIGOLO_API_TOKEN_FILE
  • Docker Compose: Use packaging/compose.serve.yml for production deployments with built-in volume persistence and health checks
  • Token methods: Use inline WIGOLO_API_TOKEN for development; use WIGOLO_API_TOKEN_FILE with bind-mounted secrets for production to avoid leaking credentials via docker inspect
  • Data persistence: Always mount a volume to /data to preserve browser binaries, models, and cache across container restarts
  • Image variants: The default image downloads the browser on first use; the full target pre-bundles it for offline environments

Frequently Asked Questions

Why does Wigolo refuse to start when I bind to 0.0.0.0?

Wigolo implements a fail-closed security model as documented in docs/self-hosting.md. When the daemon detects a non-loopback bind address (0.0.0.0 or any external IP), it validates that authentication is configured. Without WIGOLO_API_TOKEN or WIGOLO_API_TOKEN_FILE set, the process exits immediately to prevent accidental exposure of an unsecured service to the network.

How do I prevent my API token from appearing in docker inspect output?

Use the WIGOLO_API_TOKEN_FILE environment variable instead of WIGOLO_API_TOKEN. Store your token in a file on the host (e.g., /run/secrets/wigolo_token), bind-mount it as a read-only volume, and set WIGOLO_API_TOKEN_FILE to the container path. This keeps the secret out of the container's environment variables, preventing exposure through docker inspect or process enumeration.

What is the difference between the default and full Docker image targets?

The default target (defined in Dockerfile lines 5-10) provides a slim image that downloads the browser binary on first request into the persisted /data volume. The full target (lines 62-66) pre-installs the browser binary during the build process. Use the default target for standard deployments with internet access; use the full target for air-gapped environments or to eliminate first-request latency.

How do I persist data across container restarts?

Mount a named Docker volume to the /data directory inside the container. This directory stores the browser binary, on-device models, and cache. The official packaging/compose.serve.yml configures this automatically with volumes: wigolo-data:/data. When using docker run, include -v wigolo-data:/data to ensure persistence.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →