---
title: Self Hosted Docker
description: "Run the whole Executor server in one Docker container over a SQLite file: API, MCP, auth, code execution, and the web UI."
---

The self-hosted image is the entire Executor server in a single container: the
typed API, the MCP server, authentication, QuickJS code execution, and the web
console, all in one process over a libSQL (SQLite) file. There is no external
database, worker, or proxy to run.

## Run it

Using the published image:

```bash
docker run -d \
  --name executor-selfhost \
  -p 4788:4788 \
  -v executor-data:/data \
  ghcr.io/usefulsoftwareco/executor-selfhost:latest
```

Or build from a clone of the [repo](https://github.com/UsefulSoftwareCo/executor),
from `apps/host-selfhost`:

```bash
docker compose up -d --build
```

No configuration is required. Open [http://localhost:4788](http://localhost:4788);
the first person to create an account becomes the owner. After that, people join
through single-use invite links you mint from the **Admin** page, and open signup
is closed.

## Storage

Everything that must persist (the SQLite database and the generated encryption
keys) lives in the data directory, mounted at **`/data`** in the container. Always
mount a volume there so it survives restarts and upgrades:

```bash
-v executor-data:/data     # named volume (what compose uses)
# or a host path:
-v /srv/executor:/data
```

Back it up by snapshotting that volume (or copying `/data`, primarily `data.db`).
`EXECUTOR_DATA_DIR` (default `/data`) sets the directory, and `EXECUTOR_DB_PATH`
(default `<data dir>/data.db`) sets the database file.

## Environment variables

Everything is optional: a bare run boots a working instance. The defaults below are
the container defaults.

| Variable                            | Default                         | Purpose                                                                                         |
| ----------------------------------- | ------------------------------- | ----------------------------------------------------------------------------------------------- |
| `PORT`                              | `4788`                          | HTTP port the server listens on.                                                                |
| `EXECUTOR_HOST`                     | `0.0.0.0`                       | Bind address. The image binds all interfaces.                                                   |
| `EXECUTOR_DATA_DIR`                 | `/data`                         | Directory holding the database and generated keys.                                              |
| `EXECUTOR_DB_PATH`                  | `<data dir>/data.db`            | SQLite database file.                                                                           |
| `EXECUTOR_WEB_BASE_URL`             | auto (`http://localhost:4788`)  | Public URL browsers use. Required behind a domain or TLS (see below).                           |
| `EXECUTOR_TRUSTED_ORIGINS`          | unset                           | Comma-separated browser aliases allowed to authenticate without changing the public URL.        |
| `BETTER_AUTH_SECRET`                | generated, persisted in `/data` | Session secret (32+ chars). Rotating it signs everyone out.                                     |
| `EXECUTOR_SECRET_KEY`               | generated, persisted in `/data` | Master key encrypting stored secrets. Set it to manage it yourself.                             |
| `EXECUTOR_BOOTSTRAP_ADMIN_EMAIL`    | unset                           | Pre-create the admin headlessly (with the password below); skips browser first-run.             |
| `EXECUTOR_BOOTSTRAP_ADMIN_PASSWORD` | unset                           | Password for the bootstrap admin.                                                               |
| `EXECUTOR_BOOTSTRAP_ADMIN_NAME`     | `Admin`                         | Display name for the bootstrap admin.                                                           |
| `EXECUTOR_ORG_NAME`                 | `Default`                       | Display name of the single org every user joins.                                                |
| `EXECUTOR_ORG_SLUG`                 | `default`                       | URL slug for that org.                                                                          |
| `EXECUTOR_ALLOW_LOCAL_NETWORK`      | `false`                         | Allow sandboxed code to reach loopback / private addresses. Keep off unless you trust the code. |
| `EXECUTOR_DISABLE_AUTH_RATE_LIMIT`  | `false`                         | Turn off sign-in rate limiting. Only when a proxy or WAF in front of Executor limits instead.   |

Tracing is configured separately, and off unless you turn it on — see
[Tracing](/hosted/tracing).

## Behind a domain or TLS

<Warning>
  Set `EXECUTOR_WEB_BASE_URL` to the exact public URL you load in the browser (scheme, host, and
  port). If it does not match, browser logins are rejected with an invalid-origin error.
</Warning>

```bash
docker run -d \
  -p 4788:4788 \
  -v executor-data:/data \
  -e EXECUTOR_WEB_BASE_URL=https://executor.example.com \
  ghcr.io/usefulsoftwareco/executor-selfhost:latest
```

For a headless deploy (CI or infra-as-code), set `EXECUTOR_BOOTSTRAP_ADMIN_EMAIL`
and `EXECUTOR_BOOTSTRAP_ADMIN_PASSWORD` to create the admin without the browser
setup screen.

### Additional browser aliases

Keep `EXECUTOR_WEB_BASE_URL` pinned to the canonical public HTTPS origin used for
OAuth callbacks and generated links. If the same instance is intentionally
available through additional browser addresses, allow those exact origins
separately:

```bash
-e EXECUTOR_WEB_BASE_URL=https://executor.example.com \
-e EXECUTOR_TRUSTED_ORIGINS=http://executor.home.arpa:4788,http://192.0.2.10:4788
```

Only cookie-authenticated browser requests use this allowlist. OAuth callbacks,
MCP metadata, approval links, and other absolute URLs remain pinned to
`EXECUTOR_WEB_BASE_URL`. Origins are never inferred from request headers.

## Connect an agent

The server exposes a streamable-HTTP MCP endpoint at `/mcp`. Point your client at
`http://localhost:4788/mcp` (or your public URL). See [MCP Proxy](/mcp-proxy) for
how that endpoint exposes your integrations.
