> ## Documentation Index
> Fetch the complete documentation index at: https://docs.polpo.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Self-hosting

> Run the open-source Polpo runtime and single-tenant dashboard on your infrastructure.

Self-hosting runs the open-source Polpo runtime with your project directory, storage backend, model credentials, and optional single-tenant dashboard. No Polpo Cloud account is required.

## Requirements

* Docker, or Node.js 20+ with pnpm
* a `.polpo/` project directory
* access to a model gateway or provider credential
* PostgreSQL only when `storage` is set to `postgres`

## Build the Docker image

The open-source repository contains the backend Dockerfile:

```bash theme={null}
git clone https://github.com/lumea-labs/polpo.git
cd polpo
docker build -t polpo-self-hosted .
```

<Warning>
  Current release workflows publish `ghcr.io/lumea-labs/polpo-runner`, which is the sandbox runner, not the Polpo backend. Build the root Dockerfile for the self-hosted server; do not use the runner image as an API server.
</Warning>

Create a `workspace/.polpo/` project as shown in [Local Development](/docs/platform/local-development), then run:

```bash theme={null}
docker run --rm \
  -p 3890:3890 \
  -v "$PWD/workspace:/app/workspace" \
  -e POLPO_API_KEY="replace-with-a-long-random-key" \
  -e AI_GATEWAY_API_KEY="$AI_GATEWAY_API_KEY" \
  polpo-self-hosted
```

The image uses `/app/workspace` as `WORK_DIR` and listens on port `3890`.

## Runtime and dashboard with Docker Compose

The repository includes a production-oriented Compose example. It keeps PostgreSQL and the runtime on a private network and exposes only the dashboard:

```bash theme={null}
cp docker/self-host/.env.example docker/self-host/.env
```

Edit `docker/self-host/.env`. Replace `POSTGRES_PASSWORD` and `POLPO_API_KEY` with independent random values, set `POLPO_MODEL`, and provide the matching model credential. The included example uses `AI_GATEWAY_API_KEY` for Vercel AI Gateway.

```bash theme={null}
docker compose \
  --env-file docker/self-host/.env \
  -f docker/self-host/compose.example.yml \
  up --build --detach --wait
```

Open `http://localhost:3000`. The dashboard proxies runtime REST and completion requests; `POLPO_API_KEY` stays in the dashboard server process and is not shipped to browser JavaScript.

The example mounts `docker/self-host/project.example/polpo.json`, uses PostgreSQL for runtime state, and persists the workspace in a Docker volume. `DATABASE_URL` supplies `settings.databaseUrl` because it is omitted from the project file.

Stop the stack without deleting data:

```bash theme={null}
docker compose \
  --env-file docker/self-host/.env \
  -f docker/self-host/compose.example.yml \
  down
```

Add `--volumes` only when you intentionally want to delete the PostgreSQL and workspace volumes.

## Verify a checkout end to end

The repository also contains an isolated test stack with a deterministic mock model. It is separate from the production example:

```bash theme={null}
corepack enable
pnpm install --frozen-lockfile
pnpm test:self-host
```

The test builds both images, initializes disposable PostgreSQL storage, verifies authentication, creates an agent, executes a real completion path, checks session persistence and dashboard rendering, then removes its containers and volumes.

## Storage backends

| Backend    | Configuration           | Intended deployment                                    |
| ---------- | ----------------------- | ------------------------------------------------------ |
| File       | `"storage": "file"`     | Simple single-instance server                          |
| SQLite     | `"storage": "sqlite"`   | Single-instance database                               |
| PostgreSQL | `"storage": "postgres"` | Shared durable database and multi-instance deployments |

For PostgreSQL, set `settings.databaseUrl` or `DATABASE_URL`. File and SQLite storage do not require a database URL.

## Model gateway

For the default Vercel gateway, pass `AI_GATEWAY_API_KEY`. To use another OpenAI-compatible endpoint, configure its URL and key variable name in the mounted `.polpo/polpo.json`:

```json theme={null}
{
  "project": "self-hosted",
  "settings": {
    "gateway": {
      "url": "https://gateway.example.com/v1",
      "apiKeyEnv": "MY_GATEWAY_API_KEY"
    }
  }
}
```

Then pass `MY_GATEWAY_API_KEY` to the container. There is no `AI_GATEWAY_URL` server environment variable. See [Model Gateway](/docs/platform/llm-gateway) for provider overrides and local endpoints.

## Server environment

| Variable             | Default          | Description                                               |
| -------------------- | ---------------- | --------------------------------------------------------- |
| `PORT`               | `3890`           | HTTP port                                                 |
| `HOST`               | `0.0.0.0`        | Bind address in the Docker entrypoint                     |
| `WORK_DIR`           | `/app/workspace` | Directory containing `.polpo/`                            |
| `POLPO_API_KEY`      | unset            | One accepted API key                                      |
| `POLPO_API_KEYS`     | unset            | Additional comma-separated accepted API keys for rotation |
| `CORS_ORIGINS`       | unset            | Comma-separated allowed origins                           |
| `DATABASE_URL`       | unset            | PostgreSQL URL fallback                                   |
| `AI_GATEWAY_API_KEY` | unset            | Vercel AI Gateway credential                              |

Provider and custom gateway variables configured by the project must also be present in the server process.

## Authentication paths

When either API-key variable is set, chat and standard REST routes accept a Bearer token:

```bash theme={null}
curl http://localhost:3890/v1/chat/completions \
  -H "Authorization: Bearer $POLPO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"agent":"agent-1","messages":[{"role":"user","content":"Hello"}]}'
```

The legacy `x-api-key` header is also accepted by standard `/api/v1` routes:

```bash theme={null}
curl http://localhost:3890/api/v1/agents \
  -H "x-api-key: $POLPO_API_KEY"
```

## Reverse proxy

SSE responses must not be buffered. A minimal nginx location is:

```nginx theme={null}
server {
    listen 443 ssl;
    server_name polpo.example.com;

    location / {
        proxy_pass http://127.0.0.1:3890;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_buffering off;
        proxy_read_timeout 3600s;
    }
}
```

Self-hosted execution uses the runtime's local process isolation by default. The runtime accepts the portable `sandbox.isolation` policy, but the selected host adapter decides whether `reuse` and `fresh` map to separate local processes, containers, or another sandbox provider. Self-hosting does not include the managed Cloud sandbox pool, project provisioning, or operational control plane.

The open-source dashboard exposes runtime-scoped views for agents, playground chat, skills, shared memory, files, and sessions. Organization management, billing, managed connections, Cloud provisioning, and other control-plane screens remain Cloud-only.
